Kiểm tra nullptr của một con trỏ sau khi delete

Xin chào mọi người, mình muốn hỏi về vấn đề check nullptr khi đã delete.
Mình có đoạn code như sau

#include <iostream>
#include <vector>
#include <string>

using std::cout;
using std::string;

class Person {
  string name_;
  Person* friend_;
public:
  Person(string const & name) : name_(name){}

  string  name() const {return name_;}
  Person* get_friend() const { return friend_;}
  void    add_friend(Person* p) {friend_ = p;}
  bool    have_friend() const { return (friend_ != nullptr);}
};

int main() {
  Person tom{"Tom"};
  Person* new_friend = new Person{"Will"};
  tom.add_friend(new_friend);

  delete new_friend;
  
  cout << "Does " << tom.name() << " have friend?: " << ( tom.have_friend() ? ("YES") : ("NO")) << '\n';

  if(tom.have_friend()){
    // cout << tom.name() << "'s friend is " << tom.get_friend()->name() << '\n';
  }
}

Vấn đề: mặc dù đã có delete new_friend rồi nhưng với điều kiện trong hàm have_friend() chỉ là so sánh friend_ != nullptr nên hàm này vẫn trả về TRUE.
Đến đoạn in ra thì sẽ bị lỗi rồi vì access vào vùng memory đã bị delete (SEGFAULT) thông qua tom.get_friend()->name()

Thử nghiệm: Mình có thể giải quyết vấn đề này bằng việc thêm một vài member để keep track của những pointer được trỏ vào, hoặc một cách khác là sử dụng smart pointer, nhưng mình đang muốn tìm một cách giải quyết khác dựa vào những gì có sẵn.

#Update: làm rõ câu hỏi

Câu hỏi: Giả sử trong trường hợp bất khả kháng mình phải sử dụng raw pointer và ngoài 2 cách đã nêu (keep track và sử dụng smart pointer) thì C++ có feature/library/function nào để mình có thể kiểm tra được friend_ đã bị delete hay chưa. (Expectation: Sau khi gọi delete thì hàm have_friend() phải trả về là FALSE ) hoặc một cách nào khác hay không?

Chính xác thì smart pointer chính là cái library có sẵn để làm cái chuyện keep track đó thôi, nên câu hỏi của bạn nó tự mâu thuẫn nhau thì làm sao mà trả lời?

spoiler
#include <iostream>
#include <vector>
#include <string>

using std::cout;
using std::string;

class Person {
  string name_;
  Person** friend_;
public:
  Person(string const & name) : name_(name){}

  string  name() const {return name_;}
  Person* get_friend() const { return *friend_;}
  void    add_friend(Person** p) {friend_ = p;}
  bool    have_friend() const { return (*friend_ != nullptr);}
};

int main() {
  Person tom{"Tom"};
  Person* new_friend = new Person{"Will"};
  tom.add_friend(&new_friend);

  delete new_friend;
  new_friend=nullptr; // Always remember to assign nullptr after delete
  
  cout << "Does " << tom.name() << " have friend?: " << ( tom.have_friend() ? ("YES") : ("NO")) << '\n';

  if(tom.have_friend()){
    // cout << tom.name() << "'s friend is " << tom.get_friend()->name() << '\n';
  }
}
4 Likes

Phương án (PA) 1: Mình hiểu smart pointer có thể giải quyết được vấn đề này.
PA 2: Keep track thì idea chính xác như code của bạn edit.

Nhưng mình muốn hỏi là: ngoài 2 cách bên trên ra thì còn cách nào nữa hay không, nếu dựa vào những gì có sẵn thì càng tốt?

Lí do mình hỏi vậy: Giả sử mình đang có đoạn mà của một chương trình rất lớn và bây giờ gặp phải vấn đề này, hơn nữa ý tưởng và thiết kế ban đầu khá tệ khi sử dụng raw pointer. Nhưng vì lí do thời gian và công sức nên muốn hạn chế càng nhiều việc refactor càng tốt.

  • Nếu sử dụng PA 1: sẽ refactor lại toàn bộ source hiện tại, những chỗ nào sử dụng raw pointer thì chuyển sang smart pointer hết.
  • Nếu sử dụng PA 2: cũng sẽ cần refactor lại những chỗ sử dụng con trỏ 1 cấp sang con trỏ 2 cấp. Ở chỗ nào gọi delete thì thêm vào dòng gán nullptr.image

Do vấn đề này xuất phát từ ý tưởng và thiết kế ban đầu rồi, nên việc sửa chữa, bảo trì ở giai đoạn càng về sau thì càng tốn công sức.

Câu này dịch lại là thay vì tốn thời gian để refactor lại cho đúng, thì bạn chọn tốn thời gian viết thêm code để làm chương trình còn lớn hơn nữa mà còn chưa chắc sửa được hết lỗi. (Nhìn style code như này thì đảm bảo còn N lỗi khác nữa)

1 Like

Bạn chưa hiểu ý tôi thì phải?
Tôi hiểu là phải làm như vậy (refactor hay thêm code mới) để giải quyết vấn đề này.

Trong bài post này tôi muốn hỏi là: ngoài 2 cách bên trên ra thì liệu còn cách nào khác có thể giải quyết được vấn đề tôi nêu ra hay không.

Tôi miễn bàn về vấn đề này.

Câu hỏi như vậy thì đây là câu trả lời của /me rồi.

1 Like

okay, cảm ơn bạn :+1:

ở ngoài class xài shared_ptr ở trong class xài weak_ptr thôi :V

raw pointer trỏ tới cái gì thì phải bảo đảm cái được trỏ tới sống dai hơn raw pointer đó. Ko bảo đảm được thì xài smart pointers thôi :V

4 Likes

hmm, từ trước đến giờ tôi không nghĩ đến vấn đề này. Cảm ơn bác đã share :+1:

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?