C ++构造函数/析构函数inheritance

编辑:答案摘要

在下面,B是A的一个子类。

这是一个术语问题; 因为B 不能从A的接口借用,所以Ctors和dtors 不是inheritance的。 一个类至less有一个构造函数,并且只有一个析构函数。

  • 构造函数
    • B不从Ainheritance构造函数;
    • 除非B的明确地呼叫A的Ctor 之一,否则A的默认Ctor会 B的Ctor主体之前被自动调用(这个想法是A在B被创build之前需要被初始化)。
  • 破坏者
    • B不inheritanceA的dtor;
    • B退出后,B的析构函数会自动调用A的析构函数。

致谢:特别要感谢Oli Charlesworth和Kos的回答,我把Kos的答案设定为解决scheme,因为这是我最理解的。


原来的post

当您在Google上search“C ++析构函数inheritance网站:stackoverflow.com”时,您当前find以下post:

  1. 构造函数和析构函数inheritance :具有30k +声望的两个用户说它是inheritance的,而不是
  2. 虚拟析构函数是inheritance的吗? :这里没有提到会指向不被inheritance的析构函数
  3. C ++中的析构函数和inheritance? :评论似乎表明析构函数是inheritance的

Q1:我从实践中知道的是,如果不明确定义派生类的构造函数,就不能用相同的原型初始化派生对象,这是正确的吗?


即使从post中可以清楚地看到,析构函数似乎是被inheritance的,但是我仍然对这样一个事实感到困惑,那就是32k声誉的用户不会这么说。 我写了一个小例子来说明大家的想法:

#include <cstdio> /******************************/ // Base class struct A { A() { printf( "\tInstance counter = %d (ctor)\n", ++instance_counter ); } ~A() { printf( "\tInstance counter = %d (dtor)\n", --instance_counter ); } static int instance_counter; }; // Inherited class with default ctor/dtor class B : public A {}; // Inherited class with defined ctor/dtor struct C : public A { C() { printf("\tC says hi!\n"); } ~C() { printf("\tC says bye!\n"); } }; /******************************/ // Initialize counter int A::instance_counter = 0; /******************************/ // A few tests int main() { printf("Create A\n"); A a; printf("Delete A\n"); a.~A(); printf("Create B\n"); B b; printf("Delete B\n"); b.~B(); printf("Create new B stored as A*\n"); A *a_ptr = new B(); printf("Delete previous pointer\n"); delete a_ptr; printf("Create C\n"); C c; printf("Delete C\n"); c.~C(); } 

这里是输出(用g ++ 4.4.3编译):

 Create A Instance counter = 1 (ctor) Delete A Instance counter = 0 (dtor) Create B Instance counter = 1 (ctor) Delete B Instance counter = 0 (dtor) Create new B stored as A* Instance counter = 1 (ctor) Delete previous pointer Instance counter = 0 (dtor) Create C Instance counter = 1 (ctor) C says hi! Delete C C says bye! Instance counter = 0 (dtor) // We exit main() now C says bye! Instance counter = -1 (dtor) Instance counter = -2 (dtor) Instance counter = -3 (dtor) 

问题2:谁能认为没有inheritance,请解释一下?

问题3:当你用input调用子类的构造函数时会发生什么? 超类的“空构造函数”是否也被调用?

术语,术语…

好吧,“Fooinheritance”是什么意思? 我们的意思是说,如果类A对象在其接口中有Foo ,那么作为A的子类的类B对象在其接口中也具有Foo

  • 构造函数不是对象接口的一部分。 他们直接属于类。 A类和B类可以提供完全不同的构造函数集合。 没有“inheritance”在这里。

    实现细节:每个B的构造函数调用一些A的构造函数。

  • 析构函数确实是每个对象接口的一部分,因为对象的用户负责调用它们(即直接使用delete或者通过让对象超出范围来间接)。 每个对象都有一个析构函数 :它自己的析构函数,它可能是一个虚拟函数。 它一直是它自己的,它不是inheritance的。

    (实现细节:B的析构函数调用A的析构函数。)

所以:基础和派生的构造函数和析构函数之间有一个连接,但它不像“它们是inheritance的”。

我希望这回答你的想法。

Q1:我从实践中知道的是,如果不明确定义派生类的构造函数,就不能用相同的原型初始化派生对象,这是否正确?

除了在超类中定义默认构造函数的小例子之外,是的,您是正确的。


问题2:谁能认为没有inheritance,请解释一下?

这可能是术语定义的问题。 虽然很明显虚拟析构函数存在并且“按预期”工作,但我们在C ++标准([class.virtual])中看到:

即使析构函数没有inheritance ,派生类中的析构函数也会覆盖声明为虚拟的基类析构函数

(重点是我的)


问题3:当你用input调用子类的构造函数时会发生什么? 超类的“空构造函数”是否也被调用?

如果你没有明确地调用特定的超类构造函数,那么默认的超类构造函数将被调用(假设它是可见的)。

析构函数不被inheritance。 如果一个类没有定义一个,编译器会生成一个。 对于琐碎的情况,析构函数只是调用基类的析构函数,通常这意味着它的析构函数没有显式的代码(模仿inheritance)。 但是,如果一个类具有析构函数的成员,则在调用基类的析构函数之前,生成的析构函数会为这些成员调用析构函数。 这是inheritance的function不能做的事情。

inheritance是:重用和扩展现有类而不修改它们的机制,从而在它们之间产生层次关系。

inheritance就像将对象embedded到类中一样。

当类inheritance一个基类,那么基类的构造函数首先被调用,然后派生类的,而析构函数的调用是相反的。

那么为什么调用基类构造函数(称为不是inheritance可能与参数/默认值):以确保在执行派生类的构造函数时正确构造基类。

现在调用析构函数(调用不inheritance):当基础对象超出作用域时,析构函数被自己调用。因此,析构函数的inheritance问题是np问题。

现在你的问题:

答1 –是的,你对第一个问题是正确的。
ans 2 – 所以析构函数在对象的范围外出后不会被inheritance。
ans 3 –如果在派生类中,您正在使用参数进行调用,那么只有该构造函数会被调用,而没有其他构造函数会被调用。
没有任何问题的地方在于创build对象时会调用同一对象的两个构造函数,就像在创build对象时所调用的构造函数一样。 它准备了新的对象供使用,所以没有用不同的构造函数准备对象两次的逻辑。

从技术上讲,析构函数是inheritance的。 但是在正常情况下,inheritance的析构函数并不直接用于派生类; 他们被调用是因为派生类自己的析构函数调用它们来销毁它自己的“基类子对象”作为摧毁更大对象的一个​​步骤。 在非常规情况下,如果直接在派生对象上使用基类析构函数,则很难避免未定义的行为。

这个例子直接来自C ++标准(12.4p12)。

 struct B { virtual ~B() { } }; struct D : B { ~D() { } }; D D_object; typedef B B_alias; B* B_ptr = &D_object; void f() { D_object.B::~B(); // calls B's destructor B_ptr->~B(); // calls D's destructor B_ptr->~B_alias(); // calls D's destructor B_ptr->B_alias::~B(); // calls B's destructor B_ptr->B_alias::~B_alias(); // calls B's destructor } 

如果~B不是D的inheritance成员,则f的第一个陈述将是不合格的。 事实上,这是合法的C ++,尽pipe非常危险。

在你的例子中,你明确地调用了析构函数。 这是合法的(显然,因为它编译和运行),但几乎总是不正确的。

对于使用new创build的dynamic分配对象,当使用delete对象时,将运行析构函数。

对于静态分配的对象,只需在函数范围内声明对象即可创build对象,当对象的作用域消失时,将运行析构函数。 也就是说,当main()退出时,对象的析构函数将会运行。 但是您已经通过手动调用这些对象来运行这些对象的析构函数了! 这就是为什么你的例子的输出显示计数减less到-3 …你运行了两次析构函数abc

这里是相同的代码,注释显示析构函数何时会自动运行:

 int main() { printf("Create A\n"); A a; printf("Delete A\n"); a.~A(); printf("Create B\n"); B b; printf("Delete B\n"); b.~B(); printf("Create new B stored as A*\n"); A *a_ptr = new B(); printf("Delete previous pointer\n"); delete a_ptr; // Implicitly calls destructor for a_ptr. a_ptr is class B, // so it would call a_ptr->~B() if it existed. Because B is an A, after // its destructor is called, it calls the superclass's destructor, // a_ptr->~A(). printf("Create C\n"); C c; printf("Delete C\n"); c.~C(); } // Function exits here at the close brace, so anything declared in its scope is // deallocated from the stack and their destructors run. // First `c` is destroyed, which calls c.~C(), then because C is a subclass of A // calls c.~B() (which doesn't exist, so a blank implementation is used), then // because B is a subclass of A calls c.~A(). This decrements the counter, but // the count is wrong because you already manually called c.~C(), which you // ordinarily shouldn't have done. // Then `b` is destroyed, in a similar manner. Now the count is off by 2, // because you had already called b.~B(). // Lastly `a` is destroyed, just as above. And again, because you had already // called a.~A(), the count is now off by 3.