Не требуется ли созданный по умолчанию конструктор для создания всех базовых классов?

Я сталкивался со случаем, когда безопасный с точки зрения типов C++ производит несоответствующие ctor/dtors. Следующий код генерирует два конструктора для A. Конструктор по умолчанию также создает свою базу (B), но сгенерированный по умолчанию ctor копирования / перемещения не создает B. Позднее он уничтожает B, поэтому мы получаем несоответствующие ctor/dtor.

Я пробовал это с gcc и clang и оба провалились. На форуме об ошибках gcc они предположили, что это не проблема gcc. Возможно, я что-то упускаю, но разве нет ничего странного, когда код, безопасный для типов, приводит к вызову dtor для класса, который еще не был создан?

Выход программы:

B() -> INSERT: 0x7fff55398b2f
~B() -> ERASE: 0x7fff55398b2f
~B() -> ERASE: 0x7fff55398b40         // <- unmatched dtor call 
Assertion failed: (!all.empty()), function ~B, file gcc_bug.c, line 20.

Код следует:

#include <set>
#include <iostream>
#include <cstdint>
#include <cassert>
#include <experimental/optional>


std::set<std::uintptr_t> all;

struct B
{
    B()
    {
        std::cerr << "B() -> INSERT: " << this << "\n";
        all.insert((std::uintptr_t)this);
    }
    ~B()
    {
        std::cerr << "~B() -> ERASE: " << this << "\n";
        assert(!all.empty());                                // FAILS
        assert(all.find((std::uintptr_t)this) != all.end()); // FAILS
        all.erase((std::uintptr_t)this);
    }
};
struct A : B {};

static std::experimental::optional<A> f()
{
    A a;
    return a;
}

int main()
{
    auto a = f();
    return 0;
}

1 ответ

Решение

У тебя есть B создается неявно определенным конструктором копирования. Это, конечно, не зовет B::B(), Если вы добавите следующий конструктор:

B(const B& other) : B()
{
    *this = other;
}

вы увидите вывод:

B() -> INSERT: 0x7ffe57ef918f
B() -> INSERT: 0x7ffe57ef91b0
~B() -> ERASE: 0x7ffe57ef918f
~B() -> ERASE: 0x7ffe57ef91b0

Важно отметить следующее: каждый конструктор полностью создает объект. По умолчанию конструктор копирования не будет вызывать конструктор по умолчанию (и, очевидно, наоборот). Поэтому, если у вас есть что-то, что нужно сделать в каждом конструкторе, вы должны явно сделать это в каждом конструкторе, либо прямым вызовом, либо цепочкой конструктора.

Другие вопросы по тегам