我开始学习C ++并且我不明白这是内存泄漏还是某种伏都教魔法?!
我有一些"单身" class(仅用于演示):
#include <iostream>
using namespace std;
class S {
private: S() {
cout << "S::S" << endl;
}
public: static S* instance() {
static S* i;
if(i == NULL) {
cout << "Create S" << endl;
i = new S;
}
return i;
}
public: virtual ~S() {
cout << "S::~S" << endl;
}
public: void a() {
cout << "S::a" << endl;
}
};
int main() {
// call some method
S::instance()->a();
// delete pointer to instance (no reason, because I'm curious)
delete S::instance();
// call method again
S::instance()->a();
return 0;
}
这些的输出是:
Create S
S::S
S::a
S::~S
S::a
所以我的问题是:为什么在析构函数调用后我仍然在静态变量中有类S的工作副本?
更新:感谢您的回答。我意识到自己的错误。请原谅我的不安。
答案 0 :(得分:2)
对实例的第二次调用现在存在风险:
S::instance()->a();
它会打电话:
public: static S* instance() {
static S* i;
if(i == NULL) {
cout << "Create S" << endl;
i = new S;
}
return i;
}
但我不是NULL(尽管对象被删除,指向它的指针无效),所以它返回指向曾经是S
的某个内存的指针。然后你有未定义的行为,旧的对象可能在那里,它可能没有,或者更糟糕的事情可能发生。
结论要使其工作,请确保在删除对象时始终将指针设置为NULL。例如,请查看以下答案:How to delete Singleton pointer?
答案 1 :(得分:0)
您没有“工作实例”,您有未定义的行为(您在销毁对象后访问它)。只是因为a()
没有以任何方式访问对象的数据成员,它似乎正常工作。但是你在技术上取消引用一个悬空指针,任何事情都可能发生,包括段错误。
虽然你的情况稍有不同(你有一个删除后的访问权限,而不是一个悬挂的指向堆栈的指针),你可能想要在陈旧的指针上阅读这个excellent answer。
关于格式化的附注:虽然您的代码布局肯定是合法的,但它对C ++开发人员来说非常陌生。以下是更自然的格式:
class S {
S() {
cout << "S::S" << endl;
}
public:
static S* instance() {
static S* i;
if(i == NULL) {
cout << "Create S" << endl;
i = new S;
}
return i;
}
virtual ~S() {
cout << "S::~S" << endl;
}
void a() {
cout << "S::a" << endl;
}
};
答案 2 :(得分:0)
因为使用已销毁的对象会导致未定义的行为。任何事情都可能发生,包括它的工作......直到它决定不这样做。
这是语言的正确答案。这通常导致这种未定义行为的原因是因为成员函数只是常规函数,一旦变成机器代码。如果你从未真正使用过你的班级成员,那么它就不会爆炸。
但是,未定义的行为。不要这样做,期待它能够发挥作用。
答案 3 :(得分:0)
正如在其他答案中一样,您的代码中存在未定义的行为,在简单的代码中,您可能会看到您的代码无论如何都能正常运行。在大多数代码库中,代码要复杂得多,而这样的错误导致:
bash:第7行:7324分段错误(核心转储)./a.out
但是这仍然可能发生在代码的其他部分,与bug存在的部分无关,也不会立即发生。
我稍微修改了你的代码,以允许这样的输出。正如你所看到的,在这段代码中有更多的分配,这很可能导致了这个段错误,我的意思是这个向量可能正在重用部分S :: ss释放的内存,导致它在S ::中使用时崩溃转储一个。
http://coliru.stacked-crooked.com/a/c1be7a83275ae847
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class S {
std::string ss;
private: S() {
ss = "ub ub ub ub ub ub ub ub ";
cout << "S::S" << endl;
}
public: static S* instance() {
static S* i;
if(i == NULL) {
cout << "Create S" << endl;
i = new S;
}
return i;
}
public: virtual ~S() {
cout << "S::~S" << endl;
}
public: void a() {
cout << "S::a" << ss << endl;
}
};
int main() {
S::instance()->a();
// delete pointer to instance (no reason, because I'm curious)
delete S::instance();
for ( int n = 0; n < 100; ++n ){
std::vector<char> avec(n);
// call some method
S::instance()->a();
}
return 0;
}