Если класс A
содержит класс B
, то при уничтожении A
сначала будет вызван деструктор B
, т. Е. Обратный порядок их вложенных отношений.
Но что если A
содержит shared_ptr
из B
, а B
содержит необработанный указатель на A
, как нам обращаться с деструктором, чтобы сделать его безопасным?
Рассматривая следующий пример:
#include <iostream>
#include <memory>
#include <unistd.h>
struct B;
struct A {
int i = 1;
std::shared_ptr<B> b;
A() : b(std::make_shared<B>(this)) {}
~A() {
b = nullptr;
std::cout << "A destruct done" << std::endl;
}
};
struct B {
A *a;
B(A *aa) : a(aa) {}
~B() {
usleep(2000000);
std::cout << "value in A: " << a->i << std::endl;
std::cout << "B destruct done" << std::endl;
}
};
int main() {
std::cout << "Hello, World!" << std::endl;
{
A a;
}
std::cout << "done\n";
return 0;
}
Вы можете видеть в деструкторе A
, я явно установил b на nullptr
, что немедленно вызовет деструктор B
и блокирует его до конца sh. Вывод будет:
Hello, World!
value in A: 1
B destruct done
A destruct done
done
, но если я закомментирую эту строку
~A() {
// b = nullptr; // <---
std::cout << "A destruct done" << std::endl;
}
Вывод будет:
Hello, World!
A destruct done
value in A: 1
B destruct done
done
кажется, что A
Деструктор закончил, не дожидаясь разрушения B
. Но в этом случае я ожидал ошибки сегмента, поскольку, когда A
уже уничтожен, B
пытался получить доступ к члену A
, что недопустимо. Но почему программа не выдает ошибку сегмента? Это нормально (т.е. undefined behavior
)?
Кроме того, когда я изменяю
{
A a;
}
на
A * a = new A();
delete a;
, выход остается прежним , нет ошибки сегмента.