为什么构造函数在C++中被调用了两次

Why is the constructor called twice in C++?

本文关键字:两次 调用 构造函数 C++ 为什么      更新时间:2023-10-16

在这段代码中,构造函数被调用两次。

我该如何避免这种情况?

如果我取消对默认构造函数代码块的注释,则代码不会给出令人满意的输出。。

我还想要基于条件的模板实例化,所以我使用了void指针。

#include<iostream.h>
template<class Type>
class Data{
      public:
      Type val;
      Data(Type v){
                cout<<"In Constructor Param";
                val = v;
      }
      Data(){
             //  cout<<"In Constructor Defa";  uncommnet this line
      }
      ~Data(){}
};
int main(){
    Data<void *> obj;
    obj = new Data<float>(31.34f);
    cout<<*(float*)obj.val;
}

输出:

In Constructor Param
In Constructor Param
31.34

谢谢你的参与。

第一个调用应该是显而易见的:在对new Data<float>(31.34f)的调用中。

第二个是在下一行中构造的Data<void*>类型的临时对象中。您的代码等效于以下内容:

Data<void *> obj;
Data<float> *t1 = new Data<float>(31.34f); //first!
void *t2 = t1;
Data<void *> t3(t2);  //second!
Data<void *> t4(t3); //copy constructor (elided)
obj = t4;  //operator=
cout<<*(float*)obj.val;

值得注意的是,最后一行的演员阵容可能不是你想要的。那就是:

cout << ((Data<float*>)obj.val).val;

或者类似的。

编译器为您隐式声明了一个复制构造函数。接下来,您的Data可以从Type隐式转换,在void*的情况下,它可以从任何其他指针隐式转换。让我们来分解一下:

Data<void*> obj;
Data<float>* new_obj = new Data<float>(31.34f); // your call to new
                       // the constructor is called the first time here
void* conv1 = new_obj; // implicit conversion to void*
Data<void*> conv2 = conv1; // implicit conversion from 'Type' (aka 'void*')
                           // the constructor is called the second time here
obj = conv2; // copy constructor

此外,行cout<<*(float*)obj.val;调用C++98/03中的未定义行为(您的编译器似乎比它老得多),因为您的val实际上是Data<float>*,而不是float*。你应该把它作为

cout << static_cast<Data<float>*>(obj.val)->val;

因为您正在创建三个对象。您的代码包含通过转换构造函数Data<void*>::Data(void*)Data<float>*Data<void*>的隐式转换,相当于

Data<void *> obj;                              // first object
Data<float> * temp = new Data<float>(31.34f);  // second object
obj = Data<void *>((void*)temp);               // third (temporary) object

我不知道如何避免这种情况,因为我不知道你的代码试图做什么。你可以通过声明构造函数explicit来防止类似的奇怪转换,这样它就不允许隐式转换。

此外,你用来学习C++的任何一本书都已经过时了。自1998年(可能更早)以来,标准I/O头被称为<iostream>,没有.h,所有标准库的名称(如cout)都在namespace std中。

Data<void *> obj;使用传递new Data作为参数值的参数化构造函数构造一个Data对象以存储void *类型。obj本身需要是一个指针:

#include <iostream>
int main(){
    Data<float> *obj = new Data<float>(31.34f);
    std::cout << obj->val;
}

您的版本相当于:

Data<void *> objs; // no param constructor
/* Data<float>(31.34f); param constructor */
obj = Data<void *>(new Data<float>(31.34f)); // param constructor