c++ primer“ 需要自定义析构函数的类也需要自定义赋值运算符和拷贝构造函数”
当一个带有指针成员*b的类A被拷贝或赋值给B时,该指针所指向的内存多出一个指针B.b,这样,当A 对象析构时,删除了A.b所指向的内存,这时,当B析构时,利用delete 释放B.b指向的内存时发现要释放的内存不存在,会导致错误。
class Publisher {
//假设这个对象很大,需要在堆上分配内存 :Publisher *pub = new Publisher;
};
class Book {
public:
Book(Publisher *p):pub(p){}
~Book() {
delete pub;
}
private:
int val = 0;
Publisher *pub;
};
void fun(Book b) {
//对象作为实参传递给费引用形参时会调用拷贝构造函数
//函数作用域结束后,b会被释放,调用析构函数
}
int main() {
Publisher* pb = new Publisher;
Book b(pb);
fun(b);// 形参生命周期结束会释放b.pub
system("pause");
return 0;
//b的生命周期结束后析构时出错
}
运行上述代码会出现如下错误:
解决方案:智能指针
这时可以用一个类来管理这种类(我简称为C)的指针成员释放问题,称该类为U,类U(它不需要进行拷贝赋值等操作)有一个指针成员和一个计数器,该指针会被赋值为类C的指针成员,计数器初始值为1,表示某块内存肯定被指针成员指向着。
当类C的对象A拷贝给对象B时(假设A的指针成员指向sp内存),此时,sp内存会对多一个指针指向它,此时,A成员的智能指针派上用场,将A的指针计数器加1,同时将A的所有成员赋值给B,如果对象B赋值给A时(此时,B和A的指针成员不一定指向同一块内存),故A的指针成员计数器减一,B的指针成员计数器加1,类C析构时,要判断智能指针U类型的成员计数器减一后是否为0,是0则删除类C的指针成员,否则不进行删除。这样,当多次析构时,该计数器已经为负,则不会进行释放无效内存。
下面是完整代码:
#include<iostream>
#include<string>
#include<vector>
#include<memory>
using namespace std;
template <class T>
class U_ptr {
public:
int _use = 1;
U_ptr(T *_ptr):ptr(_ptr){}
~U_ptr() {
cout << "destructor" << endl;
delete ptr;
}
private:
T *ptr;
};
class Publisher {
//假设这个对象很大,需要在堆上分配内存 :Publisher *pub = new Publisher;
};
class Book {
public:
Book(Publisher *p):pub(p),ptr(new U_ptr<Publisher>(p)){}
~Book() {
//指针成员需要手动释放内存
cout << "Book destructor" << endl;
if(ptr->_use == 0)
delete ptr;
}
Book(const Book& b) {
++ptr->_use;
val = b.val;
}
Book& operator=(const Book& b) {
++b.ptr->_use;
/*这里之所以要检测ptr的计数器,是因为,
*如果两个对象的指针成员引用的不是同一块内存,
*此时,如果this.ptr指向的内存再无指针引用,就要及时删除
*/
if (--ptr->_use == 0) {
delete ptr;
}
val = b.val;
ptr = b.ptr;
}
private:
int val = 0;
Publisher *pub;
U_ptr<Publisher> *ptr;
};
void fun(Book b) {
//对象作为实参传递给费引用形参时会调用拷贝构造函数
//函数作用域结束后,b会被释放,调用析构函数
}
int main() {
Publisher* pb = new Publisher;
Book b(pb);
fun(b);
system("pause");
return 0;
}