2016-06-05 2 views
1

Следующий код проверяет пустую ссылку и создает объект с использованием new, если обнаружено.Null reference check in C++

Код компилируется, и объект создается успешно (как ожидалось), но программа неожиданно завершается в строке ref.set_dat(55) в функции main() (также показана в комментариях), что является неожиданным поведением.

Не похоже, почему метод вызывает метод set_dat() сбой при успешном создании объекта с использованием new?

class X { 
     private: 
      int *_dat,*__dat; 
     public: 
      X(); // constructor 
      ~X(); // destructor 
      int get_dat() const { 
       return *(this->_dat); 
      } 
      int get__dat() const { 
       return *(this->__dat); 
      } 
      void set_dat(int data) { 
       *(this->_dat)=data; 
      } 
      void set__dat(int data) { 
       *(this->__dat)=data; 
      } 
    }; 

    X::X() { 
     this->_dat=new int(0); // assign default value of 0 
     this->__dat=new int(0); 
     cout << "Construction Successful\n"; 
    } 

    X::~X() { 
     delete this->_dat; 
     delete this->__dat; 
     _dat=NULL; 
     __dat=NULL; 
     cout << "Destruction Successful\n"; 
    } 

    int main() { 
     X *obj=NULL; 
     X &ref=*obj; 
     if (&ref==NULL) { 
      cout << "NULL REFERENCE DETECTED\n"; 
      obj=new X; 
     } else { // this must not execute 
      cout << "YOU CANT BE HERE!!!\n"; 
     } 
     ref.set_dat(55); // Program terminates at this statement 
     cout << "Data 1 has value " << ref.get_dat() << endl; 
     delete obj; 
     cout << "Delete successful\n"; 
     obj=NULL; 
     if (&ref==NULL) { 
      cout << "NULL REFERENCE\nPROGRAM NOW TERMINATES"; 
     } else { // this block must not execute 
      ref.set_dat(58); 
      ref.set__dat(99); 
      cout << "Data 1 now is " << ref.get_dat() << endl; 
      cout << "Data 2 now is " << ref.get__dat() << endl; 
      delete obj; 
     } 
     return 0; 
    } 

Пожалуйста, обратите внимание, что я попытался подставляя ref, ссылку, с obj->, исходный объект, но безрезультатно такая же ситуация встречается; программа завершается в одной строке.

Может ли кто-нибудь объяснить мне, почему программа не может выполнить на этой конкретной линии даже после того, как создание объекта прошло успешно, а в случае неправильного синтаксиса или логики - предложите мне правильный? Ожидаемый путь программы упоминается в комментариях.

+5

'X * obj = NULL; X & ref = * obj; 'Это неопределенное поведение, полная остановка. Даже без ошибок компилятора, это неправильно, и может привести к чему угодно. Думать, что это должно делать, не имеет смысла. – deviantfan

+0

@deviantfan Я не понимаю, как это неопределенное поведение? Это вполне логично. Пожалуйста, уточните подробнее. – zeus

+1

Возможно, это логично для вас, но стандарты C++ не позволяют этого (периода). Если в стандарте что-то классифицируется как «неопределенное поведение», это означает, что почти все может произойти. Он может работать, он может дать ошибку компилятора, он может упасть, он может сжечь ваш компьютер ... вы понимаете, что я имею в виду, он может делать «что угодно». И это может изменить поведение с течением времени, etc.etc.etc. – deviantfan

ответ

7
X *obj=NULL; 
X &ref=*obj; 

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

Ваш код повторно правопреемников obj на полпути, так что единственный способ, которым ref может обновляться автоматически, если ref относится к самой obj переменной, а не к X объекта, obj точки на:

int main() { 
    X *obj = NULL; 
    X* &ref = obj; // <-- a reference to a pointer 
    if (ref == NULL) { // <-- OK 
     cout << "NULL REFERENCE DETECTED\n"; 
     obj = new X; // <-- ref can now access the X 
    } else { // this must not execute 
     cout << "YOU CANT BE HERE!!!\n"; 
    } 
    ref->set_dat(55); // <-- OK 
    cout << "Data 1 has value " << ref->get_dat() << endl; 
    delete obj; 
    cout << "Delete successful\n"; 
    obj = NULL; // <-- ref no longer accesses an X 
    if (ref == NULL) { // <-- OK 
     cout << "NULL REFERENCE\nPROGRAM NOW TERMINATES"; 
    } else { // this block must not execute 
     ref->set_dat(58); 
     ref->set__dat(99); 
     cout << "Data 1 now is " << ref->get_dat() << endl; 
     cout << "Data 2 now is " << ref->get__dat() << endl; 
     delete obj; 
    } 
    return 0; 
} 

Кроме того, изменение ref в указатель вместо ссылки:

int main() { 
    X *obj = NULL; 
    X** ref = &obj; // <-- a pointer to a pointer 
    if (*ref == NULL) { // <-- OK 
     cout << "NULL REFERENCE DETECTED\n"; 
     obj = new X; // <-- *ref can now access the X 
    } else { // this must not execute 
     cout << "YOU CANT BE HERE!!!\n"; 
    } 
    (*ref)->set_dat(55); // <-- OK 
    cout << "Data 1 has value " << (*ref)->get_dat() << endl; 
    delete obj; 
    cout << "Delete successful\n"; 
    obj = NULL; // <-- *ref no longer accesses an X 
    if (*ref == NULL) { // <-- OK 
     cout << "NULL REFERENCE\nPROGRAM NOW TERMINATES"; 
    } else { // this block must not execute 
     (*ref)->set_dat(58); 
     (*ref)->set__dat(99); 
     cout << "Data 1 now is " << (*ref)->get_dat() << endl; 
     cout << "Data 2 now is " << (*ref)->get__dat() << endl; 
     delete obj; 
    } 
    return 0; 
} 
+0

Спасибо за ответ; Это в значительной степени функционирует, как я ожидал. Значит, мы должны «никогда» не ссылаться на нулевой инициализированный объект? О, и это значит, что мой предыдущий код не смог выполнить строку 'ref.set_data (55);' потому что ref ссылался на переменную obj, а не на точку памяти, на которую указывает точка? Еще раз спасибо. – zeus

+0

'Так значит, мы должны« никогда »ссылаться на нулевой инициализированный объект« Да ». 'это означает, что мой предыдущий код не смог выполнить ... потому что ...' UB. В самом деле. Это не имеет смысла. – deviantfan

+0

@zeus: Нет такой вещи, как «NULL инициализированный объект». ** ** указатель ** на объект может быть NULL, но сам объект не может быть NULL. Ваш код не сработал, потому что '* obj' вызывал неопределенное поведение, когда' obj' был NULL, поэтому 'ref' не ссылался на действительный объект' X', '& ref' никогда не мог быть NULL, поэтому' new X' не был вызван, ref.set_dat() 'crashed пытается получить доступ к недопустимому объекту' X'. –