2011-02-05 80 views
0

我有一个关于这个问题:C++继承问题

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对象。我相信intB的构造函数中被翻译为A,但为什么?

在此先感谢。

Avri。

回答

5

因为我们还没有宣布A构造函数explicit编译器创建的A的anomymous实例使用i,并用它来初始化B实例。如果您不希望编译器执行这些隐式转换,请将您的构造函数声明为explicit。然后你会得到一个编译器错误。

1

由于没有从intA转换,暗示你的代码转换成

A* p = new B(A(i)); 
+0

伟大的tnx,所以例如,如果我添加而不是int其他类型,如char *行不会编译becasue A没有适当的c'tor来处理上述类型。 – Avri 2011-02-05 10:33:21

2

因为A有一个参数构造函数需要一个int并没有标示explicit你可以隐式转换的intA

当你这样做new B(i),因为B唯一可行的构造函数采用A,试图以i转换为A,构建不同于新B。此转换是通过使用构造函数创建临时A来完成的,该构造函数采用int

B对象被构造,所述基类是A复制从临时A这意味着从临时A复制成员变量apa构成。

严格说来,因为构造函数按值取A对象,所以在概念上,该临时对象被再次复制。然而,编译器可以通过直接从i构造B的构造函数参数来消除临时性,所以效果看起来好像只是一个副本。

这将导致一个严重的错误,因为当临时A被破坏,delete pa将导致动态分配int被摧毁,但新分配的B对象的基类将仍然有这个指针的拷贝现在没有对一个无效对象的长点。如果编译器没有删除其中一个副本,则会立即发生“双重释放”。

A的关键方面是它有一个用户定义的析构函数来执行资源操作(取消分配)。这是一个强烈的警告,A需要用户定义的复制构造函数和复制赋值运算符,因为编译器生成的版本可能不会与A的设计一致。

这被称为“三个规则”,它说如果你需要一个析构函数的用户定义版本,复制构造函数或复制赋值运算符,那么你很可能需要用户定义的所有版本他们。

如果您试图在您的示例中释放动态分配的B对象,则可能会导致“双重释放”错误。另外,A的析构函数需要标记为virtual,通过指向A的指针才能正常工作。