Деструктор не разрушает объекты

0

Я написал небольшую программу, но главный деструктор работает неправильно. Вот код программы:

#include<iostream.h>

class citizen {
private:
    char* name;
    char* nationality;
public:             
    citizen(char* name, char* nationality) {
        this->name = name;
        this->nationality = nationality;
    }                  

    citizen(const citizen &obj) {
        name = obj.name;
        nationality = obj.nationality;
    }        

    void display() {
        cout << "Name: " << name << endl;
        cout << "Nationality: " << nationality << endl;
    }

    ~citizen() { 
        if(name){
            delete[]name;
        }
        if(nationality) {
            delete []nationality;                          
        }        
    }       
};

main() {
   citizen obj1("Ali", "Pakistani");
   obj1.display();
   { 
      citizen obj2 = obj1;                 
   }
   obj1.display();
   system("pause");
}

Я знаю, что в main функции, где я назначаю состояние obj1 в obj2, с того места они оба указывают на ту же область памяти. В то время как код citizen obj2 = obj1; находится между двумя фигурными фигурными скобками.

   { 
      citizen obj2 = obj1;                 
   }

Поэтому после выполнения второй фигурной скобки obj2 должен уничтожить, а также удалить name переменных и nationality. И когда я вызываю obj1.display(); во второй раз он должен отображать мусор на экране.

Но obj1 все еще печатает точное имя, которое я предоставил в конструкторе, хотя его не должно быть.

Пожалуйста, объясните это поведение.

Теги:

7 ответов

0
Лучший ответ

Спасибо всем. Я заменил этот код конструктора

citizen(char* name, char* nationality) {
    this->name = name;
    this->nationality = nationality;
}        

с этим кодом

citizen(char* name, char* nationality){ 
   this->name = new char[strlen(name)+1]; 
   strcpy(this->name, name); 
   this->nationality = new char[strlen(nationality)+1]; 
    strcpy(this->nationality, nationality); 
} 

и, наконец, он отлично работает. благодаря

2

Ваше delete[] вызывает неопределенное поведение, потому что вы пытаетесь уничтожить строковые литералы. Все может случиться.

Даже если вы сами выделили память, вы все равно столкнетесь с неопределенным поведением, потому что будете пытаться получить доступ к уже удаленной памяти:

obj1.display();
{ 
   citizen obj2 = obj1;                 
}
obj1.display();  // ILLEGAL

Поскольку вы не определяете оператор присваивания, будет использоваться генерируемый компилятором, который просто назначает указатели на ту же память - память, которую вы уничтожаете, а затем пытаетесь получить доступ.

1

Ваш конструктор копирования просто копирует указатели (как неявный, если вы не предоставили свой собственный), что означает, что оба объекта попытаются удалить те же массивы. Кроме того, вы указываете, что указатели указывают на строковые литералы, которые не должны удаляться вообще; вы должны удалять только те объекты, которые были созданы с помощью new. Простым решением является делегирование управления памятью классу, предназначенному для этого:

std::string name;
std::string nationality;

Теперь вам не нужно беспокоиться о своем собственном деструкторе, конструкторе копирования или операторе копирования-назначения; и в качестве бонуса ваш класс также будет правильно перемещаться на С++ 11.

Если вам нравится иметь дело с проблемами памяти самостоятельно, тогда вам понадобятся ваши конструкторы для выделения новых буферов и копирования содержимого. Будьте осторожны с безопасностью исключений, поскольку вы пытаетесь манипулировать двумя отдельными динамическими ресурсами в одном классе, что всегда является рецептом ошибок. Вам также понадобится оператор присваивания копий (по правилу 3), и для эффективности вы также можете рассмотреть конструктор перемещения и оператор переадресации.

Кроме того, возможно, стоит обновить одну из версий этого языка в этом веке. <iostream.h> не был стандартным заголовком около пятнадцати лет.

1

Этот код неисправен.

     if(name){
              delete[]name;
     }
     if(nationality){                          
              delete []nationality;                          
     }   

Вы удаляете то, что вы не выделили на кучу, используя new оператор.

  • 1
    +1. Если вы не распределили что-то, не удаляйте это. Если вы что-то распределили, вам в конечном итоге придется удалить это. Однажды. Если вы не знаете, вам лучше знать, используете ли вы указатели. Более того, не используйте указатели. Используйте такие типы, как std::string которые все это знают за вас.
0

Это просто удача, что ваш второй экран вызова работает.

Как отмечает Джек Эйдли, удаление не буквально удаляет значения, просто отмечает область памяти как пригодную для использования. Таким образом, если никакое другое приложение не выделяет и не изменяет эту освобожденную область, прежние значения могут оставаться там. Кроме того, после удаления вы по-прежнему сохраняете указатели на эти адреса.

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

Чтобы предотвратить подобные ошибки, вы всегда должны назначать NULL неиспользуемым указателям, чтобы в следующий раз, когда вы получите ошибку " Нарушение прав доступа" при попытке получить к ним доступ. Некоторые компиляторы (например, MSVC) переписывают освобожденную память с сигнатурным значением (например, 0xDDDDDD), так что во время отладки вы можете легко поймать проблемы. Проверьте этот ответ за подробностями

Наконец, delete должен соответствовать new иначе поведение не определено, так что это снова просто удача. Я даже не могу запускать ваше приложение и показывать результаты. Он продолжает сбой и дает ошибки памяти.

  • 1
    спасибо, я заменил этот код гражданина-конструктора (char * name, char * национальность) {this-> name = name; это-> национальность = национальность; } с этим кодом гражданин (char * name, char * национальность) {this-> name = new char [strlen (name) +1]; strcpy (это-> имя, имя); это-> национальность = новый символ [strlen (национальность) +1]; strcpy (это-> национальность, национальность); } и, наконец, все работает нормально. Спасибо
  • 0
    да, это выглядит лучше :) кстати, вы не экспериментируете с этим, но не забудьте поместить name = NULL и nationality = NULL в деструктор, чтобы предотвратить несанкционированный доступ. Потому что доступ к свободному пространству памяти считается незаконным.
0

Другие указали на ошибку, связанную с строкой, но я думаю, что вы делаете гораздо более фундаментальную ошибку: delete не уничтожает вещи *; он просто освобождает память для повторного использования и вызывает соответствующий деструктор. Это означает, что его часто полностью можно использовать удаленные объекты после операции delete без возврата мусора.

* - В некоторых реализациях, в режиме __DEBUG он будет __DEBUG на выпущенной памяти, чтобы вы могли обнаружить эти ошибки, но это не является частью стандарта.

  • 0
    «Это означает, что часто вполне возможно использовать удаленные объекты после удаления» - не без столкновения с неопределенным поведением.
  • 0
    @LuchianGrigore: Ну, да.
Показать ещё 1 комментарий
0

вы должны только delete блок памяти, который был dynamically allocated в классе с использованием new operator

citizen(char* aname, char* anationality){

     size_t nameSize = strlen(aname)
     size_t nationalitySize = strlen(anationality)

     this->name = new char[nameSize];
     this->nationality = new char[nationalitySize];

     strcpy(this->name, aname, nameSize);
     this->name[nameSize ] = NULL;

     strcpy(this->nationality , anationality , nationalitySize);
     this->nationality [nationalitySize] = NULL;
}

если вы создаете память в constructor, то вы удаляете их в destructor. и потому, что у вас есть какое-то назначение, тогда вы должны реализовать copy constructor, WHICH создать память и скопировать данные с правой стороны operator =

конечная мысль, вам лучше использовать строковый объект, если вы не имеете дело с указателями, чтобы избежать memory leaks

Ещё вопросы

Сообщество Overcoder
Наверх
Меню