2009-03-24 6 views
267

Khi ghi đè một lớp trong C++ (với một destructor ảo) Tôi đang thực hiện destructor một lần nữa như ảo trên lớp kế thừa, nhưng tôi cần phải gọi destructor cơ sở?Tôi có cần gọi rõ ràng trình phá hủy ảo cơ sở không?

Nếu vậy tôi tưởng tượng nó là một cái gì đó như thế này ...

MyChildClass::~MyChildClass() // virtual in header 
{ 
    // Call to base destructor... 
    this->MyBaseClass::~MyBaseClass(); 

    // Some destructing specific to MyChildClass 
} 

Tôi có phải không?

Trả lời

365

Không, trình tự hủy được gọi tự động theo thứ tự xây dựng ngược lại. (Lớp cơ sở cuối cùng). Không gọi destructors lớp cơ sở.

+0

Còn về destructor ảo thuần túy thì sao? Trình liên kết của tôi đang cố gắng gọi nó ở phần cuối của hàm hủy không ảo của lớp thừa kế của tôi; – cjcurrie

+32

bạn không thể có một destructor ảo tinh khiết mà không có một cơ thể. Chỉ cần cho nó một cơ thể trống rỗng. Với một phương pháp ảo thuần túy thông thường, hàm ghi đè được gọi thay vào đó, với các hàm hủy, tất cả chúng đều được gọi, vì vậy bạn phải cung cấp một cơ thể. Các = 0 chỉ có nghĩa là nó phải được ghi đè, do đó, vẫn còn một cấu trúc hữu ích nếu bạn cần nó. –

+1

Câu hỏi này có thể liên quan và giúp [câu hỏi/15265106/c-a-missing-vtable-error] (http://stackoverflow.com/questions/15265106/c-a-missing-vtable-error). –

80

Không, bạn không cần phải gọi trình phá hủy cơ sở, một trình phá hủy cơ sở luôn được gọi cho bạn bởi trình phá hủy có nguồn gốc. Please see my related answer here for order of destruction.

Để hiểu lý do tại sao bạn muốn có một destructor ảo trong lớp cơ sở, xin vui lòng xem mã bên dưới:

class B 
{ 
public: 
    virtual ~B() 
    { 
     cout<<"B destructor"<<endl; 
    } 
}; 


class D : public B 
{ 
public: 
    virtual ~D() 
    { 
     cout<<"D destructor"<<endl; 
    } 
}; 

Khi bạn làm:

B *pD = new D(); 
delete pD; 

Sau đó, nếu bạn không có một destructor ảo trong B, chỉ ~ B() sẽ được gọi. Nhưng vì bạn có một destructor ảo, đầu tiên ~ D() sẽ được gọi, sau đó ~ B().

+12

Vui lòng bao gồm đầu ra chương trình (giả). nó sẽ giúp người đọc. –

6

No. Nó tự động được gọi.

25

Những gì người khác nói, nhưng cũng lưu ý rằng bạn không phải khai báo destructor ảo trong lớp dẫn xuất. Một khi bạn khai báo một destructor ảo, như bạn làm trong lớp cơ sở, tất cả các destructors có nguồn gốc sẽ là ảo cho dù bạn khai báo chúng có hay không. Nói cách khác:

struct A { 
    virtual ~A() {} 
}; 

struct B : public A { 
    virtual ~B() {} // this is virtual 
}; 

struct C : public A { 
    ~C() {}   // this is virtual too 
}; 
+1

nếu ~ B không được khai báo là ảo thì sao? Là ~ C vẫn còn ảo? – Will

+4

Có. Khi một phương thức ảo (bất kỳ, không chỉ là destructor) được khai báo ảo, tất cả các ghi đè của phương thức đó trong các lớp dẫn xuất đều tự động ảo. Trong trường hợp này, ngay cả khi bạn không khai báo ~ B ảo, nó vẫn còn, và như vậy là ~ C. – boycy

+1

Nhưng không giống như các phương thức ghi đè khác có cùng tên và thông số của các phương thức tương ứng của chúng trong lớp cơ sở, tên của hàm hủy là khác nhau. Có vấn đề gì không? @boycy –

9

No. Không giống như các phương pháp ảo khác, nơi bạn gọi rõ ràng phương thức cơ sở từ Derived to 'chain' cuộc gọi, trình biên dịch tạo mã để gọi hàm hủy theo thứ tự ngược lại các nhà thầu của họ đã được gọi.

2

Không, bạn không bao giờ gọi lớp destructor bese, nó luôn luôn được gọi là tự động như những người khác đã chỉ ra nhưng đây là bằng chứng của khái niệm với kết quả:

class base { 
public: 
    base() 
    { 
     cout << __FUNCTION__ << endl; 
    } 
    ~base() 
    { 
     cout << __FUNCTION__ << endl; 
    } 
}; 

class derived : public base { 
public: 
    derived() 
    { 
     cout << __FUNCTION__ << endl; 
    } 
    ~derived() 
    { 
     cout << __FUNCTION__ << endl; 
    } // adding call to base::~base() here results in double call to base destructor 
}; 


int main() 
{ 
    cout << "case 1, declared as local variable on stack" << endl << endl; 
    { 
     derived d1; 
    } 

    cout << endl << endl; 

    cout << "case 2, created using new, assigned to derive class" << endl << endl; 
    derived * d2 = new derived; 
    delete d2; 

    cout << endl << endl; 

    cout << "case 3, created with new, assigned to base class" << endl << endl; 
    base * d3 = new derived; 
    delete d3; 

    cout << endl; 

    return 0; 
} 

Đầu ra là:

case 1, declared as local variable on stack 

base::base 
derived::derived 
derived::~derived 
base::~base 


case 2, created using new, assigned to derive class 

base::base 
derived::derived 
derived::~derived 
base::~base 


case 3, created with new, assigned to base class 

base::base 
derived::derived 
base::~base 

Press any key to continue . . . 

Nếu bạn đặt trình phá hủy lớp cơ sở là ảo mà người dùng nên, thì kết quả giống như trường hợp 1 & 2.