C++ 构造函数/析构函数继承

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14184341/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 18:05:27  来源:igfitidea点击:

C++ Constructor/Destructor inheritance

c++inheritanceconstructordestructor

提问by Jonathan H

EDIT : Summary of answers

编辑:答案摘要

In the following, B is a subclass of A.

下面,B 是 A 的子类。

It's a matter of terminology; ctors and dtors are notinherited, in the sense that the ctor/dtor of B will notbe borrowed from A's interface. A class has at least one constructor, and has exactly one destructor.

这是一个术语问题;ctors 和 dtors不是继承的,因为 B 的 ctor/dtor不会从 A 的接口中借用。一个类至少有一个构造函数,并且正好有一个析构函数。

  • Constructors:
    • B does not inherit constructors from A;
    • Unless B's ctor explicitely calls one ofA's ctor, the default ctor from A will be called automatically beforeB's ctor body (the idea being that A needs to be initialized before B gets created).
  • Destructors:
    • B does not inherit A's dtor;
    • Afterit exits, B's destructor will automatically call A's destructor.
  • 构造函数
    • B 没有从 A 继承构造函数;
    • 除非 B 的 ctor 明确调用A 的 ctor之一,否则A 的默认 ctor 将B 的 ctor 主体之前自动调用(这个想法是在创建 B 之前需要初始化 A)。
  • 析构函数
    • B 不继承 A 的 dtor;
    • 退出,B的析构函数会自动调用的析构函数。

Acknowledgements:I would like to thank especially Oli Charlesworth and Kos for their answers, I set Kos' answer as the solution because it was the one I understood best.

致谢:我要特别感谢 Oli Charlesworth 和 Kos 的回答,我将 Kos 的答案设置为解决方案,因为它是我最理解的。



ORIGINAL POST

原帖

When you search for "C++ destructor inheritance site:stackoverflow.com" on Google, you currently find the following posts:

当您在 Google 上搜索“C++ 析构函数继承站点:stackoverflow.com”时,您当前会找到以下帖子:

  1. Constructor and Destructor Inheritance: two users with 30k+ reputation say that it is inherited, and that it's not
  2. Are virtual destructors inherited?: here nothing is mentioned that would point to destructors not being inherited
  3. Destructors and inheritance in C++?: The comments seem to indicate the destructors are inherited
  1. 构造函数和析构函数继承:两个30k+声望的用户说是继承的,不是的
  2. 虚拟析构函数是继承的吗?: 这里没有提到指向析构函数不被继承的内容
  3. C++中的析构函数和继承?: 注释似乎表明析构函数是继承的

Q1:What I also know from practice, is that you cannot initialize a derived object with the same prototype than it's parent constructor without explicitely defining a constructor for the derived class, is that correct?

Q1:我还从实践中了解到,如果没有明确定义派生类的构造函数,则不能使用与其父构造函数相同的原型初始化派生对象,对吗?



Even though it's rather clear from the posts that destructors seem to be inherited, I'm still puzzled by the fact that a user with 32k reputation would say its not. I wrote a little example that should clarify everyone's mind:

尽管从帖子中很清楚析构函数似乎是继承的,但我仍然对拥有 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();

}

and here is the output (compiled with g++ 4.4.3):

这是输出(使用 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)

Q2:Can anybody who thinks it's not inherited please explain that?

Q2:有人认为它不是遗传的,请解释一下吗?

Q3:So what happens when you call the constructor of a subclass with inputs? Is the "empty constructor" of the superclass called as well?

Q3:那么当你使用输入调用子类的构造函数时会发生什么?超类的“空构造函数”也被调用了吗?

采纳答案by Kos

Terminology, terminology...

术语,术语……

OK, what do we mean by "Foo is inherited"? We mean that if objects of class Ahave Fooin its interface, then objects of class Bwhich is a subclass of Aalso have Fooin its interface.

好的,我们所说的“Foo 是继承的”是什么意思?我们的意思是,如果类的对象A必须Foo在其接口,那么该类的对象B是子类A也有Foo在其接口。

  • Constructorsaren't a part of objects' interface. They belong directly to classes. Classes Aand Bmay provide completely different sets of constructors. No "being inherited" here.

    (Implementation detail: each B's constructors calls some A's constructor.)

  • Destructorsindeed are a part of each object's interface, since the object's user is responsible for calling them (i.e. directly with deleteor indirectly by letting an object out of scope). Each object has exactly one destructor: its own destructor, which might optionally be a virtual one. It is always its own, and it's not inherited.

    (Implementation detail: B's destructor calls A's destructor.)

  • 构造函数不是对象接口的一部分。它们直接属于类。类AB可能提供完全不同的构造函数集。这里没有“被继承”。

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

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

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

So: there's a connection between base and derived constructors and destructors, but it's not like "they're inherited".

所以:基类和派生构造函数和析构函数之间存在联系,但这不像“它们是继承的”。

I hope this answers what you have in mind.

我希望这能回答你的想法。

回答by Oliver Charlesworth

Q1:What I also know from practice, is that you cannot initialize a derived object with the same prototype than it's parent constructor without explicitly defining a constructor for the derived class, is that correct?

Q1:我还从实践中了解到,如果没有为派生类显式定义构造函数,则不能使用与其父构造函数相同的原型初始化派生对象,对吗?

Other than the trivial case where you've defined a default constructor in the superclass, yes you are correct.

除了您在超类中定义了默认构造函数的微不足道的情况之外,是的,您是对的。



Q2:Can anybody who thinks it's not inherited please explain that?

Q2:有人认为它不是遗传的,请解释一下吗?

This may be a matter of definitions of terminology. Whilst it's clear that virtual destructors exist and work "as expected", we see in the C++ standard ([class.virtual]):

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

Even though destructors are not inherited, a destructor in a derived class overrides a base class destructor declared virtual

即使析构函数不是继承的,派生类中的析构函数也会覆盖声明为 virtual 的基类析构函数。

(emphasis mine)

(强调我的)



Q3:So what happens when you call the constructor of a subclass with inputs? Is the "empty constructor" of the superclass called as well?

Q3:那么当你使用输入调用子类的构造函数时会发生什么?超类的“空构造函数”也被调用了吗?

If you don't explicitly invoke a specific superclass constructor, then the default superclass constructor will be called (assuming it's visible).

如果您没有显式调用特定的超类构造函数,则将调用默认的超类构造函数(假设它是可见的)。

回答by Pete Becker

Destructors are notinherited. If a class doesn't define one, the compiler generatesone. For trivial cases that destructor just calls the base class' destructor, and often that means that there is no explicit code for its destructor (which imitates inheritance). But if a class has members with destructors, the generated destructor calls destructors for those members before calling the base class' destructor. That's something that an inherited function would not do.

析构函数不是继承的。如果一个类没有定义一个,编译器就会生成一个。对于析构函数只调用基类的析构函数的微不足道的情况,这通常意味着其析构函数没有显式代码(模仿继承)。但是如果一个类的成员有析构函数,生成的析构函数会在调用基类的析构函数之前为这些成员调用析构函数。这是继承函数不会做的事情。

回答by sourcecode

Inheritance is what :mechanism of reusing and extending existing classes without modifying them, thus producing hierarchical relationships between them.

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

Inheritanceis almost like embedding an object into a class.

继承几乎就像将一个对象嵌入到一个类中。

when class is inheriting a base class then the base class's constructor is calledfirst then derived class's ,and the destructor's callis in reverse order.

当类继承基类时,首先调用基类的构造函数,然后调用派生类的构造函数,析构函数的调用顺序相反。

So Why Base Class Constructor is called (called not inherited may be with parameters/default) :to guarantees that the base class is properly constructed when the constructor for the derived class is executed.

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

Now Calling of Destructor (calling not inherit) :when base object get out of scope then the destructor is called on its own.so there is np issue of inheritance of destructor.

现在调用析构函数(调用不是继承):当基对象超出范围时,析构函数被自己调用。所以存在析构函数继承的问题。

now your questions:

现在你的问题:

ans 1 -yes you are correct for first question.
ans 2- so destructor is called not inherited after the scope of object goes out.
& ans 3 -if in derived class you are giving the call with parameters then only that constructor would get called , with it no other constructor would get called.
there is no point of issuse that 2 constructor of same object would get called on object creation,as constructor called at the creation of an object. It prepares the new object for use.so there is no logic of preparing the object twice with different constructors.

ans 1 -是的,您对第一个问题是正确的。
ans 2- 所以析构函数在对象的范围消失后被调用而不是继承。
& ans 3 -如果在派生类中您使用参数进行调用,则只会调用该构造函数,而不会调用其他构造函数。
在创建对象时调用同一对象的 2 个构造函数是没有意义的,因为在创建对象时调用构造函数。它准备新对象以供使用。因此没有使用不同构造函数两次准备对象的逻辑。

回答by aschepler

Technically, destructors ARE inherited. But in normal circumstances, the inherited destructors are not directly used for a derived class; they're invoked because the derived class's own destructor calls them in order to destroy its own "base class subobjects" as a step within destroying the larger object. And in the unusual circumstances where you do directly use a base class destructor on a derived object, it's very difficult to avoid Undefined Behavior.

从技术上讲,析构函数是继承的。但一般情况下,继承的析构函数不会直接用于派生类;它们被调用是因为派生类自己的析构函数调用它们是为了销毁它自己的“基类子对象”,作为销毁更大对象的一个​​步骤。并且在您直接在派生对象上使用基类析构函数的特殊情况下,很难避免未定义行为。

This example comes straight from the C++ Standard (12.4p12).

此示例直接来自 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
}

If ~Bwere not an inherited member of D, the first statement in fwould be ill-formed. As it is, it's legal C++, though extremely dangerous.

如果~B不是 的继承成员D,则中的第一个语句f将是格式错误的。事实上,它是合法的 C++,虽然极其危险。

回答by BigPeteB

In your example, you're explicitly calling the destructor functions. This is legal (obviously, since it compiled and ran) but almost always incorrect.

在您的示例中,您显式调用析构函数。这是合法的(显然,因为它编译并运行)但几乎总是不正确的。

For dynamically-allocated objects created with new, the destructor will be run when the objected is removed with delete.

对于使用 创建的动态分配对象new,当使用 删除对象时将运行析构函数delete

For statically-allocated objects, which are created simply by declaring the object within the scope of a function, the destructor is run when the object's scope disappears. That is, when main()exits, the objects' destructors will be run. But you've already run the destructors for those objects by calling them manually! This is why your example's output shows the count decreasing to -3... you've run the destructors for a, b, and ctwice.

对于静态分配的对象,这些对象只是通过在函数的作用域内声明对象来创建的,当对象的作用域消失时,就会运行析构函数。也就是说,当main()退出时,将运行对象的析构函数。但是您已经通过手动调用这些对象运行了析构函数!这就是为什么你的榜样的输出显示计数下降到-3 ...你已经运行的析构函数ab以及c两次。

Here's the same code, annotated to show when destructors will be automatically run:

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

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.

回答by Konstantin

I would want to express my thoughts. Creating any object is done in two stages:

1. Allocating area of memory for the object.

1. 为对象分配内存区域。

  1. Initializing this area of memory.

    The constructor of object is the function (method) of class (for this object), which initializes allocated area of memory and called automatically. The inheritance is embedding the object of the one class to the object of other class. There are plays with poiners "this" "under lid". The "this" is pass on implicitly to the method of class.

    What is happening when the code "B b" is done. Firstly the area of memory is allocated for object b. The class B has own default constructor B(), which is automatically called for initializing this memeory. B() is the function therefore the stack frame is created for working one. This constructor has address of b (implicity). But the object of A must be embedded to the object b. The object of A has no name. Constructor of B knows the noname embedded object of A must be created too (so the compiler C++ works). Therefore the constructor of class A for initializing noname embadded object of class A is called in the constructor of B. The new stack frame is called and noname object is being initialized. After that the stack frames are being closed and our object b of class B has been done. I think that address of b and the noname object coincide.

    The destructor is the method of class too. When we call ~B() the b is not destroyed. The destructor is the function called avtomatically when the object is being destroyed. But it doesn't mean that when we call the destructor the object must be destroyed. If the destructor of B is called, the stack frame is created for one. The default desructor of B knows about the noname embedded object of class A (so the compiler C++ works). Therefore the destructore calls the destructor of A.

  1. 初始化这块内存区域。

    对象的构造函数是类(对于这个对象)的函数(方法),它初始化分配的内存区域并自动调用。继承是将一个类的对象嵌入到另一个类的对象中。有“这个”“盖子下”的戏剧。“this”隐式传递给类的方法。

    代码“B b”完成后会发生什么。首先为对象b分配内存区域。类 B 有自己的默认构造函数 B(),它会自动调用以初始化此内存。B() 是函数,因此堆栈帧是为工作而创建的。此构造函数的地址为 b(隐式)。但是A的对象必须嵌入到对象b中。A 的对象没有名称。B 的构造函数知道也必须创建 A 的 noname 嵌入对象(因此编译器 C++ 可以工作)。因此,在 B 的构造函数中调用类 A 用于初始化类 A 的 noname 嵌入对象的构造函数。调用新的堆栈帧并初始化 noname 对象。之后堆栈帧被关闭,我们的 B 类对象 b 已经完成。

    析构函数也是类的方法。当我们调用 ~B() 时,b 不会被销毁。析构函数是在对象被销毁时自动调用的函数。但这并不意味着当我们调用析构函数时,对象必须被销毁。如果调用 B 的析构函数,则为一个创建堆栈帧。B 的默认析构函数知道类 A 的 noname 嵌入对象(因此编译器 C++ 可以工作)。因此析构函数调用 A 的析构函数。