第13章 拷贝控制
13.1 拷贝、赋值与销毁
13.2 拷贝控制和资源管理
两种选择:可以定义拷贝操作,使类的行为看起来像是一个值或者像一个指针。
类的行为像一个值,意味着它应该有自己的状态,当拷贝一个像值的对象时,副本和原对象是完全独立的。改变副本不会影响原对象。
类的行为像指针则是一种共享状态。当拷贝一个这种类的对象时,副本和原对象使用相同的底层数据。改变副本也会改变原对象。
13.2.1 行为像值的类
/*
给定下面的类框架,编写一个拷贝构造函数,拷贝所有成员。
你的构造函数应该动态分配一个新的string,并将对象拷贝到ps指向的位置,而不是ps本身的位置。
*/
#include<iostream>
#include<string>
using namespace std;
class HasPtr{
public:
HasPtr(const string &s=string()):ps(new string(s)),i(0){}
//拷贝构造函数 *hasPtr.ps得到字符串对象
HasPtr(const HasPtr &hasPtr):ps(new string(*hasPtr.ps)),i(hasPtr.i){}
//拷贝赋值函数
HasPtr& operator=(const HasPtr &hasPtr) {
string *new_ps=new string(*hasPtr.ps);
delete ps;
ps=new_ps;
i=hasPtr.i;
return *this;
}
//析构函数
~HasPtr(){
delete ps;
}
private:
string *ps;
int i;
};
13.2.2 定义行为像指针的类
定义一个使用引用计数的类
#include<iostream>
#include<string>
using namespace std;
class HasPtr{
public:
//将计数器置为1
HasPtr(const string &s=string()):ps(new string(s)),i(0),use(new size_t(1)){}
//拷贝三个数据成员,并递增计数器
HasPtr(const HasPtr &p):ps(p.ps),i(p.i),use(p.use){
++*use;
}
//拷贝赋值构造函数
HasPtr& operator=(const HasPtr&);
//析构函数
~HasPtr();
private:
string *ps;
int i;
size_t *use;//用来记录有多少个对象共享*ps的成员
};
HasPtr::~HasPtr(){
if(--*use==0){
delete ps;//释放string内存
delete use;//释放计数器内存
}
}
HasPtr& HasPtr::operator=(const HasPtr &rhs){
++*rhs.use;
//递减左侧对象的引用计数
if(--*this->use==0){
delete ps;
delete use;
}
ps=rhs.ps;
i=rhs.i;
use=rhs.use;
return *this;
}
13.3 交换操作
编写自己的swap函数
inline void swap(HasPtr &lhs,HasPtr &rhs){
using std::swap;
swap(lhs.ps,rhs.ps);//交换指针,而不是string成员
swap(lhs.i,rhs.i);//交换int成员
}
swap函数应该调用swap,而不是std::swap
在上面的例子中,数据成员是内置类型的,而内置类型没有特定版本的swap的,所以对swap的调用会调用标准库std::swap。
若一个类的成员有自己类型特定的swap函数,调用std::swap就是错误的了。例如有一个类Foo,它有一个类型为HasPtr的成员h
13.4 拷贝控制示例
13.5 动态内存管理类
实现标准库vector类的一个简化版本。所做的简化是不使用模板,类只用于string,命名为StrVec。