关于c ++:动态内存分配,指针成员和析构函数

dynamic memory allocation,pointer members and destructors

我编写了以下伪类来理解复制构造函数、复制分配运算符和析构函数的工作原理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#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;

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#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,所以只有保存地址的变量才会被破坏,而不是它指向的对象。

所以,是的,在每个对象中都会有内存泄漏,并且每次使用赋值操作符时,都是按照设计类的方式进行的。


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

释放发生在析构函数中,并在赋值时有条件地发生

要注意的条件是:

1
x = x;

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  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
  }


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

你可以用一个向量来实现:

1
vector <string*> container;

您可以使用它,如下所示:

1
2
3
4
5
6
7
8
// 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;