2014-10-14 4 views
-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 в статическую переменную?

Обновление: Спасибо за ответы. Я понимаю свои ошибки. Пожалуйста, простите меня за беспокойство.

+3

Вы не имеете рабочую копию, у вас есть * оборванных * указатель и действительно вызывая неопределенное поведение .. (кстати - вам не нужно 'общественности:' перед тем * каждый метод * это не java .. – Nim

+1

(btw - нисходящим избирателям - немного суровым, чтобы проголосовать за новичком - специально, без каких-либо замечаний относительно почему!) – Nim

+1

'Я не понимаю, что это утечка памяти или какой-то вид из магии вуду. Суть в том, что в отличие от большинства других языков, в C++ выполнение «плохих вещей» не означает, что код сработает, получит трассировку стека, появится окно с сообщением «вы сделали плохое», и т. д. Таким образом, ответы, которые вы получаете не только примените к вашему вопросу, но любой «ореховый» код на C++, который происходит с компиляцией без ошибок, но, похоже, работает нормально. – PaulMcKenzie

ответ

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?

+0

Мне нравится ваш ответ лучше , Вы попались на что-то, чего я пропустил, и, вероятно, это то, что OP действительно хотел узнать: почему instance() не вызвал реконструкции. –

+0

Хорошо. Я получаю это (я никогда не буду использовать такой код в реальной жизни). Но почему это не называется деструктором в конце программы (я думаю, что это должно) ?! – lxmarduk

+0

после 'S :: a' вы имеете в виду? Поскольку это указатель, вы должны очистить его самостоятельно. Ваша ОС или IDE просто освобождает память, используемую программой, не нужно вызывать деструктор. – RvdK

0

У вас нет «рабочего экземпляра», у вас есть Undefined Behavior (вы получаете доступ к объекту после его уничтожения). Так получилось, что, поскольку a() не имеет доступа к элементам данных объекта каким-либо образом, он работает нормально. Но вы технически разыгрываете висячий указатель, и все может случиться, включая segfaults.

Хотя ваш случай немного отличается (у вас есть доступ после удаления, а не оборванный указатель на стек), вы можете прочитать это 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; 
    } 
}; 
0

Поскольку использование разрушенного объекта приводит к неопределенному поведению. Все может случиться, в том числе и работать ... пока это не решит.

Это правильный ответ по языку. Причина, по которой это обычно приводит к такому неопределенному поведению, состоит в том, что функции-члены являются просто регулярными функциями, когда-то превращенными в машинный код. Если вы никогда не используете члена своего класса, он вряд ли взорвется.

Но опять же, не определено поведение. Не делайте этого, ожидая, что это сработает.

0

Как и в других ответах, у вас есть неопределенное поведение в вашем коде, в простом коде, таком как ваш, вы можете увидеть, что ваш код действительно работает в любом случае.В большинстве базы коды, код является гораздо более сложным, и такими результатами ошибок в:

Баш: Строка 7: 7324 ошибки сегментации (ядро сбрасывали) ./a.out

Но все же это может произойти в других частях кода, не связанных с тем, где присутствует ошибка, а также не сразу.

Я немного изменил ваш код, чтобы обеспечить такой выход. Как вы видите, в этом коде больше распределений, и это то, что, скорее всего, вызывает этот segfault, я имею в виду, что этот вектор, вероятно, повторно использует часть освобожденной памяти 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; 
}