GNU GCC (g++): почему он генерирует несколько dtor?


среда разработки: GNU GCC (g++) 4.1.2

пока я пытаюсь исследовать, как увеличить "покрытие кода - особенно покрытие функций" в модульном тестировании, я обнаружил, что некоторые из классов dtor, похоже, генерируются несколько раз. Кто-нибудь из вас знает почему, пожалуйста?

Я попытался и заметил, что я упомянул выше, используя следующий код.

В "тест.h"

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

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

In "испытание.ФКП"

#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;
}

когда я построил приведенный выше код (G++ test.cpp-o test), а затем посмотреть, какие символы были сгенерированы следующим образом,

nm -- demangle test

я мог видеть следующий вывод.

==== 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()

мои вопросы заключаются в следующем.

1) Почему было сгенерировано несколько dtors (BaseClass - 2, DerivedClass - 3)?

2) в чем разница между этими dtors? Как эти несколько dtors будут выборочно используется?

Теперь у меня есть ощущение, что для достижения 100% покрытия функций для проекта C++ нам нужно будет понять это, чтобы я мог вызвать все эти dtor в своих модульных тестах.

Я был бы очень признателен, если бы кто-то мог дать мне ответ на вышеизложенное.

2 76

2 ответа:

во-первых, цели этих функций описаны в Itanium C++ ABI; см. определения в разделах "базовый деструктор объектов", "полный деструктор объектов"и" удаление деструктора". Отображение на искаженные имена приведено в 5.1.4.

по сути:

  • D2-это "деструктор базового объекта". Он уничтожает сам объект, а также элементы данных и невиртуальные базовые классы.
  • D1 - это "полный деструктор объекта". Оно дополнительно уничтожает виртуальные базовые классы.
  • D0-это "удаление объекта деструктора". Он делает все, что делает деструктор полного объекта, плюс он вызывает operator delete фактически освободить память.

Если у вас нет виртуальных базовых классов, D2 и D1 идентичны; GCC будет, на достаточных уровнях оптимизации, фактически псевдонимы символов для одного и того же кода для обоих.

обычно существует два варианта конструктора (не отвечает/in-charge) и три деструктора (не отвечает/in-charge/в удаления).

The не отвечает ctor и dtor используются при обработке объекта класса, который наследуется от другого класса с помощью virtual ключевое слово, когда объект не является полным объектом (так что текущий объект не отвечает за построение или разрушение виртуального базового объекта). Этот ctor получает указатель на виртуальный базовый объект и сохраняет его.

The in-charge ctor и dtors предназначены для всех остальных случаев, т. е. если нет виртуального наследования; если класс имеет виртуальный деструктор, то в удаления указатель dtor переходит в слот vtable, в то время как область, которая знает динамический тип объекта (т. е. для объектов с автоматическая или статическая продолжительность хранения) будет использовать in-charge dtor (потому что эта память не должна быть освобождена).

пример кода:

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

результаты:

  • запись dtor в каждом из vtables для foo,baz и quux укажите на соответствующий в удаления dtor.
  • b1 и b2 построены в baz()in-charge, которая называет foo(1) in-charge
  • q1 и q2 построены в quux()in-charge, которая приходится foo(2)in-charge и baz()не отвечает с указателем foo объект, который он построил ранее
  • q2 происходит от ~auto_ptr()in-charge, который вызывает виртуальный dtor ~quux()в удаления, которая называет ~baz() не отвечает,~foo()in-charge и operator delete.
  • q1 происходит от ~quux()in-charge, которая называет ~baz()не отвечает и ~foo()in-charge
  • b2 происходит от ~auto_ptr()in-charge, который вызывает виртуальный dtor ~baz()в удаления, которая называет ~foo()in-charge и operator delete
  • b1 происходит от ~baz()in-charge, которая называет ~foo()in-charge

любой, кто происходит от quux будет не отвечает ctor и dtor и взять на себя ответственность за создание