动态内存分配、指针成员和析构函数

dynamic memory allocation,pointer members and destructors

我编写了以下虚拟程序 class 以了解复制构造函数、复制赋值运算符和析构函数的工作原理:

#include <string>
#include <iostream>

class Box {

  public:
    // default constructor
    Box(int i=10,const std::string &t=std::string()) : a(i),s(new std::string(t)) {}
    // copy constructor
    Box(const Box &other) { a=other.a; s=new std::string(*other.s); }
    // copy assignment operator
    Box &operator=(const Box &other) { a=other.a; s=new std::string(*other.s); }
    // destructor
    ~Box() { std::cout<<"running destructor num. "<<++counter<<std::endl; }
    int get_int() { return a; }
    std::string &get_string() { return *s; }
  private:
    int a;
    std::string *s;
    static int counter;

};

int Box::counter=0;

我在我的代码中使用这个 class 类型来测试它是如何工作的,但我在考虑销毁具有内置指针类型成员的对象的含义:

#include "Box.h"

using namespace std;

int main()
{
  Box b1;
  Box b2(2,"hello");
  cout<<b1.get_int()<<" "<<b1.get_string()<<endl;
  cout<<b2.get_int()<<" "<<b2.get_string()<<endl;
  Box b3=b1;
  Box b4(b2);  
  cout<<b3.get_int()<<" "<<b3.get_string()<<endl;
  cout<<b4.get_int()<<" "<<b4.get_string()<<endl;
  b1=b4;
  cout<<endl;
  cout<<b1.get_int()<<" "<<b1.get_string()<<endl; 
  {
    Box b5;
  }  // exit local scope,b5 is destroyed but string on the heap
     // pointed to by b5.s is not freed (memory leak)
  cout<<"exiting program"<<endl;
}

此指针在构造函数中初始化为指向空闲存储上的(始终是新的)动态分配的内存。因此,当调用析构函数时,要销毁的对象的成员将以相反的顺序销毁。在这种情况下,只销毁 int 和指针对象是否正确,我最终会发生内存泄漏(堆上的字符串未被释放)?

另外,定义这个复制赋值运算符,我每次赋值一个对象都会内存泄漏吗(指针指向堆上的新对象,前者丢失了是吗?)?

每次调用 new 时,都必须删除它(共享指针除外)。

所以你必须在析构函数中删除字符串。

赋值运算符适用于现有实例,因此您已经创建了 s,不必为 s 创建新字符串。

析构函数析构它的成员。由于指针就像一个 int,只有保存地址的变量被破坏,而不是它指向的对象。

所以,是的,每个对象都会有内存泄漏,每次您按照设计 class 的方式使用赋值运算符时。

处理未命名的动态分配成员的一种可能方法是每次创建它们时将它们保存在容器中(在对象、函数等中),然后 运行 一个 for 使用 delete 语句在析构函数中循环,后跟容器的元素。

你可以用向量来做到这一点:

vector <string*> container;

您可以按如下方式使用它:

// define it in the private members of your class
vector <string*> container;

// use it when you create the string
container.push_back(new dynamicallyAllocatedObject);

// free memory in the destructor
for(auto it = container.begin(); it != container.end(); ++it) delete *it;

请记住,分配发生在基础构造、复制构造和令人惊讶的条件分配上

解除分配发生在析构函数中并有条件地分配

需要注意的条件是:

x = x;

因此您的代码可以更改为以下模式(在您无法使用首选的适当智能指针的情况下)

  Box(int i=10,const std::string &t=std::string()) : a(i),s(new std::string(t)) {}
// copy constructor
  Box(const Box &other) { cp(other); }
// copy assignment operator
  Box &operator=(const Box &other) {
    if (&other != this)  // guard against self assignment
    {
       rm();
       cp(other);
    }
    return *this;
  }
// destructor
  ~Box() { rm();  }
private:
  void cp(const Box &other) {a=other.a; s=new std::string(*other.s);
  void rm() {
    std::cout<<"running destructor num. "<<++counter<<std::endl;
    delete s;    // prevents leaks
  }