0

我对此有一个问题:

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编译的最后一行吗?我传递了一个intintoB的构造函数,它需要一个A对象。我相信 theint被翻译成AinB的构造函数,但为什么呢?

提前致谢。

艾薇儿

4

3 回答 3

5

由于您尚未将A构造函数声明为explicit编译器,因此正在创建一个A使用i并使用它来初始化实例的异常B实例。如果您不希望编译器执行这些隐式转换,请将您的构造函数声明为explicit. 然后你会得到一个编译器错误。

于 2011-02-05T10:24:49.430 回答
2

因为A有一个单参数构造函数,它接受一个int并且没有被标记explicit,你可以隐式地将一个转换int为一个A

当你这样做时new B(i),因为唯一可行的构造函数B需要 a A,所以会尝试转换i为 anA并从中构造新B的。这种转换是通过A使用带有int.

构造对象时B,基类A是从临时对象中复制构造的,这意味着从临时对象中A复制成员变量。apaA

严格来说,因为构造A函数按值获取对象,所以从概念上讲,临时对象被再次复制。B但是,编译器可能会通过直接构造构造函数参数来消除临时变量,i因此效果可能看起来只是一个副本。

这将导致一个严重的错误,因为当临时A对象被销毁时,delete pa将导致动态分配int的对象被销毁,但新分配对象的基类 AB仍将具有此指针的副本,该指针现在不再指向无效对象。如果编译器没有消除其中一个副本,则会立即发生“双重释放”。

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

这被称为“三规则”,它表示如果您需要析构函数、复制构造函数或复制赋值运算符之一的用户定义版本,那么您可能需要所有这些的用户定义版本。

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

于 2011-02-05T10:32:26.173 回答
1

由于存在从intto的转换A,因此您的代码被隐式转换为

 A* p = new B(A(i));
于 2011-02-05T10:27:12.867 回答