在已删除对象上调用静态函数

时间:2014-10-14 09:43:50

标签: c++ delete-operator static-functions

我开始学习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的工作副本?

更新:感谢您的回答。我意识到自己的错误。请原谅我的不安。

4 个答案:

答案 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;
}