我对此有一个问题:
class A
{
int a;
int* pa;
public:
A(int i):a(i) , pa(new int(a))
{
cout<<"A ctor"<<a<<endl;
}
~A()
{
delete pa;
cout<<"dtor\n";
}
int * &get()
{
return pa;
}
};
class B : public A
{
int b;
public:
B (A obj): A(obj) , b(0)
{
cout<<"B ctor\n";
}
~B()
{
cout<<"B dtor\n";
}
};
int main()
{
int i = 23 ;
A* p = new B(i);
}能告诉我为什么要编译main中的最后一行吗?我把一个int传递给B的构造函数,它需要一个A对象,我相信在B的构造函数中int会被转换成A,但为什么呢?
提前谢谢。
艾芙莉。
发布于 2011-02-05 18:24:49
由于您尚未将A构造函数声明为explicit,因此编译器将使用i创建一个异常的A实例,并使用它来初始化B实例。如果您不希望编译器执行这些隐式转换,请将您的构造器声明为explicit。然后你会得到一个编译器错误。
发布于 2011-02-05 18:32:26
因为A有一个接受int的单参数构造函数,并且没有被标记为explicit,所以您可以隐式地将int转换为A。
执行new B(i)时,因为B唯一可行的构造函数接受A,所以会尝试将i转换为A并从中构造新的B。这种转换是通过使用接受int的构造函数创建一个临时A来完成的。
当构造B对象时,基类A是从临时A复制构造的,这意味着从临时A复制成员变量a和pa。
严格地说,因为构造函数通过值接受A对象,所以从概念上讲,将再次复制临时对象。然而,编译器可以通过直接从i构造B的构造函数参数来消除临时,因此效果看起来很可能只是一个副本。
这将导致严重错误,因为当临时A被销毁时,delete pa将导致动态分配的int被销毁,但新分配的B对象的基类A仍将具有该指针的副本,该副本现在不再指向无效对象。如果编译器没有消除其中一个副本,“双重释放”将立即发生。
A的关键方面是它有一个用户定义的析构函数,用于执行资源操作(释放)。这是一个强烈的警告,因为编译器生成的版本可能与A的设计不一致,所以A需要用户定义的复制构造函数和复制赋值运算符。
这就是所谓的“三个规则”,即如果你需要解构函数、复制构造函数或复制赋值操作符中的一个的用户定义版本,那么你很可能需要所有这些函数的用户定义版本。
如果您试图在示例中释放动态分配的B对象,则很可能会导致“双重释放”错误。此外,A的析构函数需要标记为virtual,以便通过指向A的指针进行删除才能正常工作。
发布于 2011-02-05 18:27:13
由于存在从int到A的转换,因此您的代码将被隐式地转换为
A* p = new B(A(i));https://stackoverflow.com/questions/4906301
复制相似问题