小心指针被delete两次

时间:2023-12-23 13:47:44

C++类中,有时候使用到传值调用(对象实体做参数),遇到这种情况,可要小心了!特别是当你所传值的对象生命周期较长,而非临时对象(生命周期段)的时候。来看看下面的情况:

#include <iostream>
using namespace std;
class Text
{
private:
    char * str;
public:
    Text(){str = new char[20];::memset(str,0,20);}
    void SetText(char * str)
    {
        strcpy(this->str,str);
    }
    char * GetText() const{return str;}
    ~Text()
    {
        cout << "~Text Destruction" << endl;
        delete [] str;
        cout << "~Text Over" << endl;
    }
};
void Print(Text str)
{
    cout << str.GetText() << endl;
}
int main()
{
    Text t;
    t.SetText("abc");
    Print(t);
    return 1;
}

上面执行的结果程序崩溃了。原因:

Print(Text str)在对str进行复制构造的时候,没有进行深度拷贝;当 Print退出的时候,因为是临时对象(函数初始时构造),对str进行析构,此时还没有任何破绽;但回到main,继而退出main 的时候,又对t进行析构,但此时t内的str中的内容已经被销毁。由于对一内存空间实施了两次销毁,于是出现内存出错。

解决方法:

    1. 重写浅拷贝。像一下版本,不同的情况要作出适当的调整:
      #include <iostream>
      using namespace std;
      class Text
      {
      private:
          char * str;
      public:
          Text(){str = new char[20];::memset(str,0,20);}
          Text(Text &t)
          {
              str = new char[20];
              strcpy(str,t.GetText());
          }
          void SetText(char * str)
          {
              strcpy(this->str,str);
          }
          char * GetText() const{return str;}
          ~Text()
          {
              cout << "~Text Destruction" << endl;
              delete [] str;
              cout << "~Text Over" << endl;
          }
      };
      void Print(Text str)
      {
          cout << str.GetText() << endl;
      }
      int main()
      {
          Text t;
          t.SetText("abc");
          Print(t);
          return 1;
      }
    2. (推荐)不使用传值调用。就像下面书写如下Print版本: 
      void Print(Text &str)
      {
          cout << str.GetText() << endl;
      }
    3. 除非对象内所有的成员读属非指针内存内容,那么谨慎使用文章前面的用法。