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 } |
处理动态分配的未命名成员的一种可能方法是在每次创建成员时(在对象、函数等中)将其保存在容器中,然后在析构函数中运行
你可以用一个向量来实现:
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; |