2012-06-15 1 views
2

나는 가상 소멸자와 약간의 실험을하고 있었다 검토 - 궁금 사람이 다음 (2010 대 사용)에 대한 간단한 설명이있는 경우 :4 레벨 상속 체인에있는 C++ 가상 소멸자.

내가 클래스 계층 구조 ABCD 정의를, D는 C가 상속, C는 B가, B는 상속 상속 A, A는 기본입니다. -

A는 가상 소멸자를 가지고

첫 번째 실험 :

는이 실험을 달렸다.

B는 --------------

C가

D가 아닌 가상 소멸자에게

//를 갖는 가상 소멸자를 갖는 비 가상 소멸자 갖는다 --------------

D 유형의 힙에 4 개의 개체 할당 - 처음에는 A *, B * 및 C * 포인터를 가리킴 - 4 번째는 a로 지정 D * 완전성. 4 개의 포인터를 모두 삭제하십시오.

예상대로 모든 4 개의 인스턴스에서 전체 소멸자 체인이 D에서 A로 역순으로 실행되어 모든 메모리가 해제됩니다.

번째 실험 -

A는 비가 상 소멸자 갖는다 ** 비 가상

B에 A를

C 가상 소멸자에게

D를 가지는 비 가상 소멸자 변경됨 비 가상 Distructor가 있음

D 유형의 힙에 4 개의 객체를 할당합니다. Point - 첫 번째 3 - Leave에서 A *, B * 및 C *의 포인터 완전성을위한 D *로 네 번째.

C * 및 D * 포인터를 삭제하면 : 완전한 소멸자 체인은 D에서 A로 역순으로 실행되어 모든 메모리가 해제됩니다.

B의 * 삭제 : B 다음 소멸자가 실행되는 (누설)

삭제의 A * : 만 소멸자가 실행되는 (누설)이 왜

사람이 설명 할 수 있습니까?

실험 2에서 D 유형 opjects가 할당되면 직접 기본 클래스 (C)에 가상 소멸자가 있습니다. 컴파일러에게이를 Vptr로 추적하고 메모리 유형을 알 수 있습니까? 참조에 관계없이? vptr에서 그것을 추적하고 메모리를 알고 컴파일러에게 나던 -

감사 마이크

D 형 opjects는 실험 2에 할당
+1

예제 코드를 제공하면 멋질 것입니다. – Rook

+0

@Rook이 사례를 설명하는 코드 스 니펫을 게시했습니다 (거의 동일하게). 의견이 있으면 알려주십시오. –

답변

6

은 즉각적인 기본 클래스 (C)는 가상 소멸자를 가지고 유형? 참조에 관계없이?

두 번째 테스트 케이스에서 제

, AB는 vptrs/vtable을 할 수 없습니다. (심지어 가상 멤버 함수가 아닌 경우에도 동적이 아닌 정적으로 고정 된 정적 멤버 함수가 여전히 정적으로 처리됩니다.) 다른 방법으로 기본 클래스는 함수의 파생 여부와 같은 정보를 "상속"하지 않습니다. 수업.

+0

기본 클래스가 파생 클래스에서 정보를 상속하지 않는 경우, 실험 1에서 D 객체에 대한 B 참조 (B가 비상 가상 소멸자 있음)가 여전히 D 객체 할당을 "알고"있고 전체 소멸자 시퀀스를 실행하는 이유는 무엇입니까? D 형 개체에 대한? – MikeyG

+2

첫 번째 테스트 케이스에서,'B'의 소멸자 **는 ** 가상이기 때문에; 멤버 함수가 기본 클래스에서 가상이라면 명시 적으로 선언하지 않아도 파생 클래스에서 가상입니다. –

+0

오케이, 이것이 내 오해가있는 부분입니다. - – MikeyG

1

가상 대 비상 가상 소멸자를 사용하는 이유는 무엇입니까? 왜냐하면 비상 가상 소멸자가있는 기본 클래스를 갖는 것은 좋지 않습니다. the faq

+0

아니, 그게 문제가 아니야. –

+0

비상 가상 소멸자가 가져 오는 문제의 데모처럼 많이 읽습니다. 그가 묘사하는 모든 행동은 완전히 기대되는 것처럼 보인다. – Rook

+0

아니요 -하지만 faq의 메모가 누락되었습니다. - – MikeyG

2

가상 소멸자없이 A *를 삭제하면 컴파일 타임에 컴파일러는 런타임시 가상 소멸자가있는 객체를 가리키는 지 알 수 없습니다. 삭제는 가상 소멸자가있는 객체 일 수도 있고 그렇지 않을 수도 있습니다. 동적 바인딩은 발생하지 않습니다.

+1

OK - 그렇다면 참조 (유형 A 포인터이므로)는 컴파일 타임에 가상 소멸자가있는 객체를 가리키고 있다는 것을 모릅니다. – MikeyG

+0

(미안하지만 위의 미완성 주석) - 아직, 실험 1에서 컴파일시 B 객체는 기본 클래스에 가상 소멸자가 있습니다. 따라서 B가 D를 가리킬 때 동적 바인딩이 발생합니까? 이것으로 다음을 결론 지을 수 있습니까? 1 - 동적 바인딩을 결정할 참조 (포인터 유형)입니다. 실제로 할당 된 메모리가 아닙니다. 2 - 상속 체인 어딘가에 컴파일 타임에 가상 소멸자가있는 한 동적 바인딩이 발생합니까? – MikeyG

+0

무시 - 지금 감사합니다. – MikeyG

1

나는 거의 동일한 질문을 구성하여 공유하고 싶다고 생각했습니다.

다른 Ctor에서 가상 함수의 일부 사용법을 추가했음을 유의하십시오 (곧 각 Ctor에서 V-table은 "최대"로만 업데이트 됨) 가상 함수를 의미합니다 호출 될 구현은 상속 체인의 "이 시점"에서 가장 많이 파생됩니다.

내 노트 : 주어진 클래스를 실행하는 샘플 코드에서 ""에 대한 모든 고려 사항을 강조하기 위해 스택에 "파생 된"개체 (B 및 D) Dtor 's의 "virtual-ness"는 클래스 인스턴스에 포인터를 사용할 때 적용됩니다.

class A; 

void callBack(A const& a); 

class A 
{ 

    public: 
    A() { std::cout << "A Ctor " << std::endl; f1(); callBack(*this); /* f3();*/ } 
    ~A() { std::cout << "A Dtor " << std::endl; } 

    void f1() { std::cout << "A : f1 " << std::endl; } 
    virtual void f2() const { std::cout << "A : f2 " << std::endl; } 
    virtual void f3() = 0; 
}; 


class B : public A 
{ 
    public: 
    B() { std::cout << "B Ctor " << std::endl; f1(); callBack(*this); f3(); } 
    ~B() { std::cout << "B Dtor " << std::endl; } 
    void f1() { std::cout << "B : f1 " << std::endl;} 
    void f2() const { std::cout << "B : f2 " << std::endl; } 
    virtual void f3() { std::cout << "B : f3 " << std::endl; } 

}; 


class C : public A 
{ 
    public: 
    C() { std::cout << "C Ctor " << std::endl; f1(); callBack(*this); f3(); } 
    virtual ~C() { std::cout << "C Dtor " << std::endl; } 
    void f1() { std::cout << "C : f1" << std::endl;} 
    void f2() const { std::cout << "C : f2" << std::endl; } 
    virtual void f3() const { std::cout << "C : f3" << std::endl; } 

}; 

class D : public C 
{ 
    public: 
    D() { std::cout << "D Ctor " << std::endl; f1(); callBack(*this); } 
    ~D() { std::cout << "D Dtor " << std::endl; } 
    void f1() { std::cout << "D : f1" << std::endl; } 
    void f2() const { std::cout << "D : f2 " << std::endl; } 
    virtual void f3() { std::cout << "D : f3 " << std::endl; } 

}; 

void callBack(A const& a) { a.f2(); } 

// ================================================================================================================================= 

int main() 
{ 
    std::cout << "Start of main program" << std::endl; 

    std::cout << "Creating a D object on the heap" << std::endl; 
    D* pd = new D; 
    C* pc = new D; 
    A* pa = new D; 

    if (true) 
    { 
     std::cout << "Entering Dummy scope # 1 and creating B object on the stack" << std::endl; 
     B b; 
     std::cout << "Leaving Dummy scope # 1 with B object within it" << std::endl; 
    } 

    if (true) 
    { 
     std::cout << "Entering Dummy scope # 2 and creating D object on the stack" << std::endl; 
     D d; 
     std::cout << "Leaving Dummy scope # 2 with D object within it" << std::endl; 
    } 

    std::cout << "Calling delete on pd (D*) which points on a D object" << std::endl; 
    delete pd; 

    std::cout << "Calling delete on pc (C*) which points on a D object" << std::endl; 
    delete pc; 

    std::cout << "Calling delete on pa (A*) which points on a D object" << std::endl; 
    delete pa; 

    std::cout << "End of main program" << std::endl; 
    return 0; 
}