Я бы начал изучать 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 в статической переменной?
Upd: Спасибо за ответы. Я осознаю свои ошибки. Пожалуйста, прости меня за беспокойство.
Второй вызов экземпляра теперь рискован:
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. Например, проверьте этот ответ: Как удалить синглтон указатель?
У вас нет «рабочего экземпляра», у вас есть неопределенное поведение (вы получаете доступ к объекту после его уничтожения). Так получилось, что потому что 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;
}
};
Потому что использование разрушенного объекта приводит к неопределенному поведению. Может случиться что угодно, в том числе и работать … пока не решится.
Это правильный ответ по языку. Причина, по которой это обычно приводит к такому неопределенному поведению, заключается в том, что функции-члены являются просто обычными функциями, однажды превращенными в машинный код. Если вы никогда не используете члена своего класса, он вряд ли взорвется.
Но опять же неопределенное поведение. Не делайте этого, ожидая, что это сработает.
Как и в других ответах, у вас есть неопределенное поведение в вашем коде, в простом коде, подобном вашему, вы можете увидеть, что ваш код в действительности работает в любом случае. В большинстве кодовых баз код намного сложнее, и такая ошибка приводит к:
bash: строка 7: 7324 Ошибка сегментации (ядро сброшено) ./a.out
Но все же это может произойти в других частях кода, не связанных с той, где присутствует ошибка, а также не сразу.
Я немного изменил ваш код, чтобы учесть такой вывод. Как вы видите, в этом коде больше выделений, и именно это, скорее всего, и вызывает этот segfault, поскольку я имею в виду, что vector, вероятно, повторно использует часть освобожденной памяти 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;
}