IT TIP

C ++ 생성자 / 소멸자 상속

itqueen 2021. 1. 8. 22:39
반응형

C ++ 생성자 / 소멸자 상속


편집 : 답변 요약

다음에서 B는 A의 하위 클래스입니다.

용어의 문제입니다. ctors 및 DTORS가되어 있지 B의 ctor에 / dtor가된다는 의미에서, 상속 하지 A의 인터페이스에서 빌릴 수. 클래스에는 최소한 하나의 생성자가 있으며 정확히 하나의 소멸자가 있습니다.

  • 생성자 :
    • B는 A로부터 생성자를 상속하지 않습니다.
    • B의 ctor A의 ctor 중 하나를 명시 적으로 호출하지 않는 한, A의 기본 ctor는 B의 ctor 본체 보다 먼저 자동으로 호출됩니다 (B가 생성되기 전에 A를 초기화해야한다는 생각입니다).
  • 소멸자 :
    • B는 A의 dtor를 상속하지 않습니다.
    • 종료 B의 소멸자는 자동으로 A의 소멸자를 호출합니다.

감사의 말 : 특히 Oli Charlesworth와 Kos의 답변에 감사하고 싶습니다. Kos의 답변은 제가 가장 잘 이해 한 것이기 때문에 해결책으로 설정했습니다.


원래 포스트

Google에서 "C ++ 소멸자 상속 site : stackoverflow.com"을 검색하면 현재 다음 게시물을 찾을 수 있습니다.

  1. Constructor and Destructor Inheritance : 평판이 3 만 이상인 두 명의 사용자가 상속되었다고 말하고
  2. 가상 소멸자는 상속됩니까? : 여기에는 상속되지 않는 소멸자를 가리키는 언급이 없습니다.
  3. C ++의 소멸자와 상속? : 주석은 소멸자가 상속되었음을 나타내는 것 같습니다.

Q1 : 제가 실제로 알고있는 것은 파생 클래스에 대한 생성자를 명시 적으로 정의하지 않으면 부모 생성자와 동일한 프로토 타입으로 파생 개체를 초기화 할 수 없다는 것입니다. 맞습니까?


게시물에서 소멸자가 상속 된 것처럼 보이는 것이 분명하지만, 평판이 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)

Q2 : 유전되지 않는다고 생각하는 사람은 설명해 주시겠습니까?

Q3 : 입력이있는 서브 클래스의 생성자를 호출하면 어떻게됩니까? 수퍼 클래스의 "빈 생성자"도 호출됩니까?


용어, 용어 ...

좋습니다. "Foo가 상 속됨"이란 무엇을 의미합니까? 우리는 클래스의 객체가 있다면 의미 AFoo그 인터페이스에서, 다음 클래스의 객체 B의 하위 클래스 A도이 Foo자사의 인터페이스.

  • 생성자 는 객체 인터페이스의 일부가 아닙니다. 그들은 수업에 직접 속합니다. 클래스 AB완전히 다른 생성자 집합을 제공 할 수 있습니다. 여기에는 "상속되는"것이 없습니다.

    ( 구현 세부 사항 : 각 B의 생성자는 A의 생성자를 호출합니다. )

  • 소멸자는 실제로 각 개체의 인터페이스의 일부입니다. 개체의 사용자가 개체를 호출 할 책임이 있기 때문입니다 (즉 delete, 개체를 범위에서 벗어나도록 직접 또는 간접적으로). 각 개체에는 정확히 하나의 소멸자가 있습니다 . 자체 소멸자는 선택적으로 가상 일 수 있습니다. 그것은 항상 자신의 것이며 상속되지 않습니다.

    (구현 세부 정보 : B의 ​​소멸자는 A의 소멸자를 호출합니다.)

그래서 : 기본 생성자와 파생 생성자와 소멸자 사이에 연결이 있지만 "상속 된"것과는 다릅니다.

이것이 당신이 염두에 둔 것에 대한 답이되기를 바랍니다.


Q1 : 제가 실제로 알고있는 것은 파생 클래스에 대한 생성자를 명시 적으로 정의하지 않으면 부모 생성자와 동일한 프로토 타입으로 파생 개체를 초기화 할 수 없다는 것입니다. 맞습니까?

슈퍼 클래스에서 기본 생성자를 정의한 사소한 경우를 제외하고는 맞습니다.


Q2 : 유전되지 않는다고 생각하는 사람은 설명해 주시겠습니까?

이것은 용어 정의의 문제 일 수 있습니다. 가상 소멸자가 존재하고 "예상대로"작동한다는 것은 분명하지만 C ++ 표준 ([class.virtual])에서 볼 수 있습니다.

소멸자가 상속되지 않더라도 파생 클래스의 소멸자는 가상으로 선언 된 기본 클래스 소멸자를 재정의합니다.

(강조 광산)


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

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


Destructors are not inherited. If a class doesn't define one, the compiler generates one. 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.


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

Inheritance is almost like embedding an object into a class.

when class is inheriting a base class then the base class's constructor is called first then derived class's ,and the destructor's call is 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.


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).

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 ~B were not an inherited member of D, the first statement in f would be ill-formed. As it is, it's legal C++, though extremely dangerous.


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.

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 c twice.

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.

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

1. Allocating area of memory for the object.

  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.

ReferenceURL : https://stackoverflow.com/questions/14184341/c-constructor-destructor-inheritance

반응형