当我尝试通过静态函数调用LeakySingleton
在堆上构造一个对象create_instance()
并尝试将其删除时,我对为什么析构函数调用自己无休止的次数感到困惑然后通过delete
操作明确显示。
据我了解,考虑下面的源清单,leaky_singleton
中的变量main()
指向create_instance()
返回的堆分配资源。因此,我们通过LeakySingleton
函数间接地在堆上分配了对象create_instance
。
现在,如果我显式调用leaky_singleton
上的delete运算符或delete函数,则它将首先调用析构函数,并检查其是否满足instance != nullptr
条件,然后删除{{1 }}点应删除。
如果此对象instance
被删除,则dtor没有理由再次调用自身,或者我在这里丢失了什么东西吗?
使用和不使用valgrind调用它会导致分段错误(由于堆栈溢出而导致无效的内存访问):
LeakySingleton::instance
单步调试器会导致无休止的析构函数调用(堆栈溢出的元凶)。
从cplusplus.com(http://www.cplusplus.com/forum/general/40044/):
如果删除对象,它将尝试删除自身,这将删除 使它尝试删除自身,这将导致它删除 本身,这将会...
当我仅使用Segmentation fault (core dumped)
运算符/函数来释放静态类成员变量delete
所指向的堆对象LeakySingleton
时,为什么它试图删除自身?
堆分配的资源由指向LeakySingleton::instance
对象的LeakySingleton::instance
指针变量指向。那么,为什么显式的LeakySingleton
函数调用不会删除或取消分配已分配的堆对象,而是无限递归?我在这里想念什么?
(我目前对dtor和ctor的理解:delete
函数/操作符为堆上的对象分配内存并调用构造函数,而new
函数调用析构函数,在我的情况下还调用内部的delete
运算符/函数。)
来源:
main.cpp :
delete
制作文件:
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;
}
答案 0 :(得分:2)
您的周期令人讨厌,在LeakySingleton::create_instance
中,您有:
instance = new LeakySingleton{t_x};
然后在LeakySingleton
的销毁器中拥有:
delete instance;
在将任何内容设置为null之前,它将调用LeakySingleton
的析构函数:
instance = nullptr;
因此您的无限递归会导致堆栈溢出。
答案 1 :(得分:2)
在C ++中,delete
将调用类析构函数。
您的delete
函数中的main
语句正在调用LeakySingleton::~LeakySingleton
,这反过来又试图删除静态实例指针,然后再次调用析构函数。您的代码永远没有机会将静态指针设置为null。那里有无限循环。
P.S。恕我直言,在非静态方法中修改静态成员通常是一个坏习惯。我相信您可以将静态清除逻辑放在另一个静态方法中。
class LeakySingleton final {
public:
static LeakySingleton& create_instance(int);
static void destroy_instance();
~LeakySinglton() = default;
private:
static LeakySingleton *instance;
...
};
void LeakySingleton::destroy_instance() {
if (instance != nullptr) {
delete instance;
instance = nullptr;
}
}
答案 2 :(得分:0)
在析构函数中删除实例,启动一个析构函数调用。
答案 3 :(得分:0)
首先,由于LeakySingleton
不能直接创建,因此也不能直接销毁它:
delete_instance()
来删除单例如果您希望实例指针泄漏并允许销毁它,则您不应执行两次(一次在析构函数之外,一次在析构函数内部),而应只在析构函数之外进行一次。由于只有一个实例,所以外面的析构函数意味着不需要在里面进行删除调用:
LeakySingleton::~LeakySingleton()
{
if (instance != nullptr)
{
instance = nullptr; // since there's only one, it's the instance and
} // the instance pointer shall be reset
// and do what's needed to clean the object
}
注意:此实现不是线程安全的。
注意2:this article可能会让您感兴趣。它还警告不要破坏公共空间,因为这可能导致指针悬空。