Меня смущает, почему деструктор бесконечно много раз вызывает сам себя, когда я пытаюсь создать объект, скажем LeakySingleton
, в куче с помощью статического вызова функции create_instance()
, а затем пытаюсь впоследствии удалить его явно черезdelete
операция.
Насколько я понимаю, учитывая списки источников снизу, переменная leaky_singleton
внутри main()
указывает на выделенный ресурс кучи, возвращаемый create_instance()
. Таким образом, мы определили объект LeakySingleton
в куче косвенно через функцию create_instance
. Теперь, если я явно вызываю оператор удаления или функцию удаления в leaky_singleton
, то сначала он вызывает деструктор и проверяет, удовлетворяет ли он условию instance != nullptr
, а затем следует удалить объект, на который указывает instance
. удален. Если этот объект LeakySingleton::instance
удален, то у dtor нет причин снова вызывать себя, или я что-то здесь упускаю?
Вызов его с использованием и без использования valgrind приводит к ошибке сегментации (неверный доступ к памятииз-за переполнения стека):
Segmentation fault (core dumped)
Прохождение через отладчик приводит к бесконечным вызовам деструкторов (виновник переполнения стека).
Из cplusplus. com (http://www.cplusplus.com/forum/general/40044/):
Если вы удаляете свой объект, он пытается удалить себя, что приведет к попытке удаления самого себя, что приведет к его удалению, что приведет к ...
Почему он пытается удалить себя, когда я просто использую оператор / функцию delete
для освобождения объекта кучи LeakySingleton
, на который указывает статическая переменная-член класса LeakySingleton::instance
? Распределенная кучана ресурс указывает переменная-указатель LeakySingleton::instance
, которая указывает на объект LeakySingleton
. Так почему явный вызов функции delete
не удаляет или скорее освобождает выделениеd куча объектов и вместо этого бесконечно повторяется? Что мне здесь не хватает?
(Мое текущее понимание dtor и ctor: функция / оператор new
выделяет память для объекта в куче и вызывает конструктор, а функция delete
вызывает деструктор ив моем случае также вызывает delete
оператор / функцию внутри.)
Источник:
main.cpp :
class Singleton final
{
public:
static Singleton & create_instance(int);
~Singleton() = default;
private:
int x;
Singleton(int);
Singleton(Singleton &) = delete;
Singleton(Singleton &&) = delete;
Singleton & operator=(Singleton &) = delete;
Singleton & operator=(Singleton &&) = delete;
};
Singleton::Singleton(int t_x) : x{t_x}
{}
Singleton & Singleton::create_instance(int t_x)
{
static Singleton instance{t_x};
return instance;
}
// Potential endless dtor calls inside:
class LeakySingleton final
{
public:
static LeakySingleton * create_instance(int);
~LeakySingleton();
private:
int x;
static LeakySingleton * instance;
LeakySingleton(int);
LeakySingleton(LeakySingleton &) = delete;
LeakySingleton(LeakySingleton &&) = delete;
LeakySingleton & operator=(LeakySingleton &) = delete;
LeakySingleton & operator=(LeakySingleton &&) = delete;
};
LeakySingleton * LeakySingleton::instance = nullptr;
LeakySingleton::LeakySingleton(int t_x) : x{t_x}
{}
LeakySingleton::~LeakySingleton()
{
if (instance != nullptr)
{
delete instance;
instance = nullptr;
}
}
LeakySingleton * LeakySingleton::create_instance(int t_x)
{
if (instance == nullptr)
{
instance = new LeakySingleton{t_x};
}
return instance;
}
int main()
{
// The correct implementation with no issues:
{
Singleton & singleton = Singleton::create_instance(42);
}
// The faulty implementation causing the dtor to recurse endlessly and resulting in a segfault:
{
LeakySingleton * leaky_singleton = LeakySingleton::create_instance(42);
delete leaky_singleton;
}
return 0;
}
Makefile :
CC = g++
CFLAGS = -g -Wall -Wextra -pedantic -std=c++11
SRC = main.cpp
TARGET = app
RM = rm -rf
.PHONY: all clean
all: $(TARGET)
clean:
$(RM) $(TARGET)
$(TARGET): $(SRC)
$(CC) $(CFLAGS) $^ -o $@