2017-06-15 30 views
17

objdump tệp .o của tôi cho thấy rằng tôi có hai trình phá hủy khác nhau cho cùng một lớp. Tại sao?Tại sao tôi có hai triển khai destructor trong đầu ra lắp ráp của tôi?

Disassembly of section .text._ZN1AD0Ev: 

0000000000000000 <_ZN1AD0Ev>: 
    0: 53      push %rbx 
    1: be 00 00 00 00   mov $0x0,%esi 
    6: 48 89 fb    mov %rdi,%rbx 
    9: 48 c7 07 00 00 00 00 movq $0x0,(%rdi) 
    10: ba 2c 00 00 00   mov $0x2c,%edx 
    15: bf 00 00 00 00   mov $0x0,%edi 
    1a: e8 00 00 00 00   callq 1f <_ZN1AD0Ev+0x1f> 
    1f: 48 89 df    mov %rbx,%rdi 
    22: be 08 00 00 00   mov $0x8,%esi 
    27: 5b      pop %rbx 
    28: e9 00 00 00 00   jmpq 2d <_ZN1AD0Ev+0x2d> 

Disassembly of section .text._ZN1AD2Ev: 

0000000000000000 <_ZN1AD1Ev>: 
    0: 48 c7 07 00 00 00 00 movq $0x0,(%rdi) 
    7: ba 2c 00 00 00   mov $0x2c,%edx 
    c: be 00 00 00 00   mov $0x0,%esi 
    11: bf 00 00 00 00   mov $0x0,%edi 
    16: e9 00 00 00 00   jmpq 1b <_ZN1AD1Ev+0x1b> 

Đây là các lớp trong file header mà kết quả trong mã này được tạo ra:

#include <iostream> 

class A { 
public: 
    virtual ~A() { 
     ::std::cout << "This destructor does something significant.\n"; 
    } 
}; 

class B : public A { 
public: 
    inline virtual ~B() = 0; 
}; 

B::~B() = default; 

class C : public B { 
public: 
    inline virtual ~C() = default; 
}; 
+1

Bạn có thể dán lớp thực tế không? –

+2

Nhiều trình biên dịch tạo ra hai trình phá hủy khác nhau cho một lớp: một để phá hủy các đối tượng được cấp phát động, một đối tượng khác để hủy các đối tượng không động (các đối tượng tĩnh hoặc địa phương hoặc các đối tượng con). Các cựu gọi 'nhà điều hành xóa' từ bên trong, sau này không. Một số trình biên dịch làm điều đó bằng cách thêm một tham số ẩn cho một destructor (phiên bản cũ của GCC làm điều đó), một số trình biên dịch chỉ đơn giản là tạo ra hai destructors riêng biệt (phiên bản mới hơn của GCC làm điều đó). Xem, ví dụ: tại đây https://stackoverflow.com/questions/7199360/what-is-the-branch-in-the-destructor-reported-by-gcov/7199706#7199706. – AnT

+0

@AnT Nghe như một câu trả lời! – Omnifarious

Trả lời

27

Nhiều trình biên dịch tạo ra hai hàm hủy khác nhau cho một lớp: một cho phá hủy đối tượng được cấp phát động, một - cho phá hủy các đối tượng không động (đối tượng tĩnh, đối tượng cục bộ, đối tượng con cơ sở hoặc đối tượng phụ thành viên). Các cựu gọi operator delete từ bên trong, sau này không. Một số trình biên dịch làm điều đó bằng cách thêm một tham số ẩn vào một destructor (phiên bản cũ của GCC làm theo cách đó, MSVC++ làm theo cách đó), một số trình biên dịch đơn giản tạo ra hai destructors riêng biệt (phiên bản mới hơn của GCC làm theo cách đó).

Sự cần thiết phải gọi operator delete từ bên trong destructor phát sinh từ đặc điểm kỹ thuật C++, mà nói rằng thích hợp operator delete nên được chọn "như thể" nó đã được nhìn lên từ bên trong (có thể ảo) destructor của đối tượng nhất. Vì vậy, operator delete, có thể được triển khai dưới dạng chức năng thành viên tĩnh sẽ hoạt động như thể nó là một hàm ảo.

Hầu hết các triển khai thực hiện yêu cầu này "nghĩa đen": chúng không chỉ tra cứu đúng operator delete từ bên trong trình phá hủy, chúng thực sự là gọi từ số từ đó.

Tất nhiên, operator delete chỉ phải được gọi từ trình phá hủy của đối tượng dẫn xuất nhất, và chỉ khi đối tượng đó được phân bổ động. Đây là nơi mà tham số ẩn (hoặc hai phiên bản của destructor) đi vào hình ảnh.

+1

Cả hai câu trả lời của bạn đều tuyệt vời vì nhiều lý do khác nhau. – Omnifarious

+0

Câu trả lời của bạn là cơ hội tốt nhất để giáo dục một người có ít kinh nghiệm hơn. Tôi sẽ chọn nó. – Omnifarious

26

GCC follows the Itanium ABI:

Bắt đầu với GCC 3.2, GCC ước nhị phân cho C++ được dựa trên một văn bản, nhà cung cấp trung lập C++ ABI được thiết kế để được cụ thể cho Itanium 64-bit ...

Các Itanium ABI định hủy khác nhau:

<ctor-dtor-name> ::= C1 # complete object constructor 
      ::= C2 # base object constructor 
      ::= C3 # complete object allocating constructor 
      ::= D0 # deleting destructor 
      ::= D1 # complete object destructor 
      ::= D2 # base object destructor 

Quy ước số có thể được nhìn thấy trong lắp ráp của bạn ra đặt (sự khác biệt giữa tên mangling trong hai hàm là 0 và 1).

Cuối cùng, đây là một mô tả về sự khác biệt giữa hai hàm hủy sau đây:

Các mục cho destructors ảo thực sự cặp mục. destructor đầu tiên, được gọi là destructor đối tượng hoàn chỉnh, thực hiện hủy diệt mà không cần gọi delete() trên đối tượng. Thứ hai destructor, được gọi là xóa destructor, gọi delete() sau khi phá hủy đối tượng.Cả hai đều phá hủy bất kỳ cơ sở ảo nào; một, chức năng không ảo riêng biệt, được gọi là destructor đối tượng cơ sở, thực hiện tàn phá của đối tượng nhưng không subobjects cơ sở ảo của nó, và không gọi xóa()

Hơn nữa, điều này chỉ xảy ra nếu lớp học của bạn có một destructor ảo:

ABI này không yêu cầu tạo hoặc sử dụng phân bổ hàm tạo hoặc xóa hủy cho lớp mà không có trình phá hủy ảo. Tuy nhiên, nếu thực hiện phát ra các chức năng như vậy, nó phải sử dụng các tên bên ngoài được chỉ định trong ABI này. Nếu một hàm như vậy có liên kết bên ngoài, nó phải được phát ra ở bất cứ nơi nào được tham chiếu, trong một nhóm COMDAT có tên là tên bên ngoài của hàm.

+1

Cả hai câu trả lời của bạn đều tuyệt vời vì nhiều lý do khác nhau. – Omnifarious

Các vấn đề liên quan