C++ 有指针成员的类如何拷贝、赋值该类对象

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++ 有指针成员的类如何拷贝、赋值该类对象_第1张图片

解决方案:智能指针

这时可以用一个类来管理这种类(我简称为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
#include
#include
#include
using namespace std;
template 
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(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 *ptr;

};
void fun(Book b) {
	//对象作为实参传递给费引用形参时会调用拷贝构造函数
	//函数作用域结束后,b会被释放,调用析构函数
}
int main() {
	Publisher* pb = new Publisher;
	Book b(pb);
	fun(b);
	system("pause");
	return 0;
}

 

 

你可能感兴趣的:(c++)