-1

我会开始学习 C++,但我不明白这是内存泄漏还是某种巫术?!

我有一些“单例”类(仅用于演示):

#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 的工作副本?

更新:感谢您的回答。我意识到我的错误。请原谅我的打扰。

4

4 回答 4

2

现在第二次调用实例是有风险的:

S::instance()->a();

它将调用:

public: static S* instance() {
    static S* i;
    if(i == NULL) {
            cout << "Create S" << endl;
        i = new S;
    }
    return i;
}

但是 i 不是 NULL (尽管对象被删除,指向它的指针无效),所以它返回一个指向曾经是的某个内存的指针S。然后你有未定义的行为,旧对象可能存在,它可能不存在,或者可能发生更糟糕的事情。

结论要使其正常工作,请确保在删除对象时始终将指针设置为 NULL。例如检查这个答案:如何删除单例指针?

于 2014-10-14T09:52:17.453 回答
0

您没有“工作实例”,您有未定义的行为(您在对象被销毁后访问它)。碰巧的是,由于a()不以任何方式访问对象的数据成员,因此它似乎可以正常工作。但是从技术上讲,您正在取消引用一个悬空指针,并且任何事情都可能发生,包括段错误。

虽然您的情况略有不同(您有一个删除后访问,而不是指向堆栈的悬空指针),但您可能想阅读这个关于过时指针的优秀答案。


关于格式化的旁注:虽然你的代码布局当然是合法的,但它对 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;
    }
};
于 2014-10-14T09:52:44.620 回答
0

因为使用被破坏的对象会导致未定义的行为。任何事情都可能发生,包括它的工作......直到它决定不这样做。

这就是语言的正确答案。这通常导致这种未定义行为的原因是因为成员函数一旦变成机器代码就只是常规函数。如果您从未真正使用过您班级的成员,那么它不太可能爆炸。

但同样,未定义的行为。不要期望它会起作用。

于 2014-10-14T09:53:33.790 回答
0

与其他答案一样,您的代码中有未定义的行为,在像您这样的简单代码中,您可能会看到您的代码实际上可以正常工作。在大多数代码库中,代码要复杂得多,这样的错误会导致:

bash:第 7 行:7324 分段错误(核心转储)./a.out

但这仍然可能发生在代码的其他部分,与存在错误的部分无关,也不会立即发生。

我已经稍微修改了您的代码,以允许这样的输出。如您所见,此代码中有更多分配,这很可能是导致此段错误的原因,我的意思是,向量可能正在重用 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;
}
于 2014-10-14T10:20:39.110 回答