Pertanyaan GNU GCC (g ++): Mengapa menghasilkan banyak dtors?


Mengembangkan lingkungan: GNU GCC (g ++) 4.1.2

Sementara saya mencoba menyelidiki bagaimana meningkatkan 'cakupan kode - terutama cakupan fungsi' dalam pengujian unit, saya telah menemukan bahwa beberapa dtor kelas tampaknya dihasilkan beberapa kali. Apakah beberapa dari Anda memiliki ide tentang mengapa?

Saya mencoba dan mengamati apa yang saya sebutkan di atas dengan menggunakan kode berikut.

Di "test.h"

class BaseClass
{
public:
    ~BaseClass();
    void someMethod();
};

class DerivedClass : public BaseClass
{
public:
    virtual ~DerivedClass();
    virtual void someMethod();
};

Di "test.cpp"

#include <iostream>
#include "test.h"

BaseClass::~BaseClass()
{
    std::cout << "BaseClass dtor invoked" << std::endl;
}

void BaseClass::someMethod()
{
    std::cout << "Base class method" << std::endl;
}

DerivedClass::~DerivedClass()
{
    std::cout << "DerivedClass dtor invoked" << std::endl;
}

void DerivedClass::someMethod()
{
    std::cout << "Derived class method" << std::endl;
}

int main()
{
    BaseClass* b_ptr = new BaseClass;
    b_ptr->someMethod();
    delete b_ptr;
}

Ketika saya membangun kode di atas (g + + test.cpp -o test) dan kemudian melihat simbol seperti apa yang telah dihasilkan sebagai berikut,

nm --klik yang menarik

Saya bisa melihat output berikut.

==== following is partial output ====
08048816 T DerivedClass::someMethod()
08048922 T DerivedClass::~DerivedClass()
080489aa T DerivedClass::~DerivedClass()
08048a32 T DerivedClass::~DerivedClass()
08048842 T BaseClass::someMethod()
0804886e T BaseClass::~BaseClass()
080488f6 T BaseClass::~BaseClass()

Pertanyaan saya adalah sebagai berikut.

1) Mengapa beberapa dtors telah dihasilkan (BaseClass - 2, DerivedClass - 3)?

2) Apa perbedaan antara para dermawan ini? Bagaimana beberapa Docs itu akan digunakan secara selektif?

Saya sekarang memiliki perasaan bahwa untuk mencapai cakupan fungsi 100% untuk proyek C ++, kami perlu memahami ini sehingga saya dapat memanggil semua DOKER dalam pengujian unit saya.

Saya akan sangat menghargai jika seseorang dapat memberi saya jawaban di atas.


76
2017-07-07 16:24


asal


Jawaban:


Pertama, tujuan fungsi-fungsi ini dijelaskan dalam Itanium C ++ ABI; lihat definisi di bawah "destruktor objek dasar", "destruktor objek lengkap", dan "menghapus destruktor". Pemetaan ke nama yang rusak diberikan dalam 5.1.4.

Pada dasarnya:

  • D2 adalah "destruktor objek dasar". Ini menghancurkan objek itu sendiri, serta anggota data dan kelas dasar non-virtual.
  • D1 adalah "destruktor objek lengkap". Ini juga menghancurkan kelas dasar virtual.
  • D0 adalah "menghapus destruktor objek". Ia melakukan segala hal yang dilakukan oleh perusak objek lengkap, ditambah lagi panggilannya operator delete untuk benar-benar membebaskan memori.

Jika Anda tidak memiliki kelas dasar virtual, D2 dan D1 identik; GCC akan, pada tingkat optimalisasi yang memadai, sebenarnya alias simbol untuk kode yang sama untuk keduanya.


58
2017-07-07 17:06



Biasanya ada dua varian konstruktor (tidak di-charge / bertanggung jawab) dan tiga dari destructor (tidak di-charge / bertanggung jawab / di-charge menghapus).

Itu tidak di-charge ctor dan dtor digunakan ketika menangani suatu objek dari kelas yang mewarisi dari kelas lain menggunakan virtual kata kunci, ketika objek bukan objek lengkap (sehingga objek saat ini adalah "tidak bertanggung jawab" dari membangun atau merusak objek basis virtual). Ctor ini menerima pointer ke objek basis virtual dan menyimpannya.

Itu bertanggung jawab ctor dan dtors untuk semua kasus lain, yaitu jika tidak ada warisan virtual yang terlibat; jika kelas memiliki destruktor virtual, di-charge menghapus pointer dtor masuk ke slot vtable, sementara ruang lingkup yang mengetahui jenis dinamis objek (yaitu untuk objek dengan durasi penyimpanan otomatis atau statis) akan menggunakan bertanggung jawab dtor (karena memori ini tidak boleh dibebaskan).

Contoh kode:

struct foo {
    foo(int);
    virtual ~foo(void);
    int bar;
};

struct baz : virtual foo {
    baz(void);
    virtual ~baz(void);
};

struct quux : baz {
    quux(void);
    virtual ~quux(void);
};

foo::foo(int i) { bar = i; }
foo::~foo(void) { return; }

baz::baz(void) : foo(1) { return; }
baz::~baz(void) { return; }

quux::quux(void) : foo(2), baz() { return; }
quux::~quux(void) { return; }

baz b1;
std::auto_ptr<foo> b2(new baz);
quux q1;
std::auto_ptr<foo> q2(new quux);

Hasil:

  • Entri entri di masing-masing vtables untuk foo, baz dan quux titik di masing-masing di-charge menghapus dtor.
  • b1 dan b2 dibangun oleh baz()  bertanggung jawab, yang memanggil foo(1)  bertanggung jawab
  • q1 dan q2 dibangun oleh quux()  bertanggung jawab, yang jatuh foo(2)  bertanggung jawab dan baz()  tidak di-charge dengan penunjuk ke foo objek itu dibangun sebelumnya
  • q2 dirusak oleh ~auto_ptr()  bertanggung jawab, yang memanggil dtor virtual ~quux()  di-charge menghapus, yang memanggil ~baz()  tidak di-charge, ~foo()  bertanggung jawab dan operator delete.
  • q1 dirusak oleh ~quux()  bertanggung jawab, yang memanggil ~baz()  tidak di-charge dan ~foo()  bertanggung jawab
  • b2 dirusak oleh ~auto_ptr()  bertanggung jawab, yang memanggil dtor virtual ~baz()  di-charge menghapus, yang memanggil ~foo()  bertanggung jawab dan operator delete
  • b1 dirusak oleh ~baz()  bertanggung jawab, yang memanggil ~foo()  bertanggung jawab

Siapa pun yang berasal quux akan menggunakan nya tidak di-charge ctor dan dtor dan mengambil tanggung jawab menciptakan foo obyek.

Pada prinsipnya, the tidak di-charge varian tidak pernah diperlukan untuk kelas yang tidak memiliki basis virtual; dalam hal ini, itu bertanggung jawab Varian inilah yang kadang-kadang disebut bersatu, dan / atau simbol untuk keduanya bertanggung jawab dan tidak di-charge adalah alias untuk implementasi tunggal.


33
2017-07-07 17:48