Object 实际上在 C++ 中何时被销毁? delete(ptr) 有什么作用?

When Does Object Actually Gets Destroyed in C++? What does delete(ptr) Do?

没有delete的程序运行完美。但是,在使用 delete 时,我没有得到任何结果。程序突然终止。

#include<iostream>
using namespace std;

class Book
{
  string *title;
  int *price,*stock;

public:
  Book()
  {
    title=new string();
    price=new int();
    stock=new int();
  }

  Book(string n,int p,int s)
  {    
     title=new string(n);
     price=new int(p);
     stock=new int(s);
  }

  ~Book()
  {
     cout<<"Object Destroyed"<<endl;
     // delete (title);  Using Delete i am getting Wrong Results.
     // delete (price);
     // delete (stock);
  }

  void inst();
  void buy();
  void display();
};

void Book::display()
{
  cout<<"Title :"<<*title;
  cout<<" Price is :"<<*price;
  cout<<" Stock is :"<<*stock;
  cout<<endl;
}

int main()
{
  Book a[2];

  for(int x=0;x<2;x++)
  {
    string title;
    int p,s;
    cout<<"Enter title,price,stock respectively"<<endl;
    cin>>title;
    cin>>p;
    cin>>s;
    a[x]=Book(title,p,s);
  }

  for(int x=0;x<2;x++)
    a[x].display();
}

以下是输出:

Enter title,price,stock respectively
C++
120
2
Object Destroyed
Enter title,price,stock respectively
JAVA
150
5
Object Destroyed
Title :C++   Price is :120    Stock is :2
Title :JAVA  Price is :150    Stock is :5
Object Destroyed
Object Destroyed

为什么每次输入后我都会得到 Object Destroyed

使用 a[x]=Book(title,p,s) 程序执行两个步骤:

  1. 使用构造函数创建 Book 的实例。让我们称这个实例为“alpha”
  2. 使用复制构造函数创建 Book (a[x]) 的新实例。让我们称这个实例为“测试版”

到达for括号的末尾时,“alpha”生命线结束,调用析构函数。

还记得拷贝构造函数吗?使用它复制指针。没有创建成员变量的新实例。令人反感的删除部分,那些成员变量在“alpha”的生命线结束时被删除,并调用相应的析构函数。尝试访问仍由“beta”指针引用的已删除变量会导致崩溃。

有多种解决方案可以解决评论中突出显示的问题:

  1. 不要使用指针
  2. 实现拷贝构造函数

Book 没有关注 Rule of 3/5/0

你有一个默认构造函数和一个转换构造函数来分配内存,一个析构函数来释放内存,但你没有复制构造函数或复制赋值运算符(或移动构造函数或移动赋值运算符)。因此,编译器将提供隐式实现,将指针按原样从一个对象浅复制到另一个对象,而不是对所指向的数据进行深度复制。

这条语句:

Book a[2];

默认构造2个Book个对象,并为每个对象分配数据。

这条语句:

a[x]=Book(title,p,s);

正在为用户输入构造一个临时 Book对象,然后将该临时对象复制分配给数组中的现有对象,最后销毁该临时对象, 释放它指向的内存。

编译器生成的复制赋值运算符将指针浅复制到另一个对象(泄漏它已经指向的内存),因此当临时释放内存时,分配给对象中的指针是悬而未决。当 display() 尝试访问无效内存时,您的代码就会崩溃。

试试这个:

#include <iostream>
#include <algorithm>
using namespace std;

class Book
{
  string *title;
  int *price, *stock;

public:
  Book() :
    title(new string()),
    price(new int()),
    stock(new int())
  {
  }

  Book(const Book &src) :
    title(new string(*src.title)),
    price(new int(*src.price)),
    stock(new int(*src.stock))
  {
  }

  // C++11 and later only...
  Book(Book &&src) :
    title(nullptr),
    price(nullptr),
    stock(nullptr)
  {
    swap(title, src.title);
    swap(price, src.price);
    swap(stock, src.stock);
  }

  Book(string n, int p, int s) :
    title(new string(n)),
    price(new int(p)),
    stock(new int(s))
  {    
  }

  ~Book()
  {
     delete title;
     delete price;
     delete stock;
  }

  Book& operator=(const Book &rhs)
  {
    if (&rhs != this)
    {
      Book tmp(rhs);
      swap(title, tmp.title);
      swap(price, tmp.price);
      swap(stock, tmp.stock);
    }
    return *this;
  }

  // C++11 and later only...
  Book& operator=(Book &&rhs)
  {
    swap(title, rhs.title);
    swap(price, rhs.price);
    swap(stock, rhs.stock);
    return *this;
  }

  ...

  void display();
};

void Book::display()
{
  cout << "Title :" << *title;
  cout << " Price is :" << *price;
  cout << " Stock is :" << *stock;
  cout << endl;
}

更简单的解决方案是争取零规则 - 编写不需要自定义 copy/move 构造函数、copy/move 运算符或析构函数的代码。让编译器生成的实现为您完成所有必要的工作:

#include <iostream>
using namespace std;

class Book
{
  string title;
  int price, stock;

public:
  Book() :
    title(), price(0), stock(0)
  {
  }

  Book(string n, int p, int s) :
    title(n), price(p), stock(s)
  {    
  }

  ...

  void display();
};

void Book::display()
{
  cout << "Title :" << title;
  cout << " Price is :" << price;
  cout << " Stock is :" << stock;
  cout << endl;
}