类的行为:
- 像值
- 像指针
- 类的行为像值,意味着它应该也有自己的状态。当我们拷贝一个像值的对象时,副本和原对象时完全独立的。改变副本不会对原对象有任何影响,反之亦然。
- 行为像指针的类则共享状态。当我们拷贝一个这种类的对象时,副本和原对象使用相同的底层数据。改变副本也会改变原对象,反之亦然。
行为像值的类
//HasPtrAsVal.h
class HasPtrAsVal
{
public:
HasPtrAsVal(const std::string &s = std::string()) :ps(new std::string(s)), i(0)
{
}
//对ps 指向的string,每个HasPtrVal对象都有自己的拷贝
HasPtrAsVal(const HasPtrAsVal &p) :ps(new std::string(*p.ps)), i(p.i)
{
}
HasPtrAsVal& operator=(const HasPtrAsVal &);
~HasPtrAsVal()
{
delete ps;
}
private:
std::string *ps;
int i;
};
//HasPtrAsVal.cpp
//当编写一个赋值运算符时,一个好的模式是先将右侧运算对象拷贝到一个局部临时对象中。
//当拷贝完成后,销毁左侧运算对象的现有成员就是安全的了。
//一旦左侧运算对象的资源被销毁,就只剩下将数据从临时对象拷贝到左侧运算对象的成员中了。
HasPtrAsVal& HasPtrAsVal::operator=(const HasPtrAsVal &rhs)
{
auto newp = new string(*rhs.ps);//拷贝底层string
delete ps;//释放旧内存
ps = newp;//从右侧运算对象拷贝数据到本对象
i = rhs.i;
return *this;
}
行为像指针的类
令一个类展现类似指针的行为的最好方法是使用shared_ptr来管理类中的资源。
但是,有时我们希望直接管理资源。在这种情况下,引用计数就相当有用。用它来记录有多少用户共享它指向的对象,当没有用户使用对象时,释放资源。
//HasPtrAsPtr.h
class HasPtrAsPtr
{
public:
//构造函数分配新的string和新的计数器,将计数器置1
HasPtrAsPtr(const std::string& s = std::string()) :ps(new std::string(s)), i(0), use(new std::size_t(1))
{
};
HasPtrAsPtr(const HasPtrAsPtr &p) :ps(p.ps), i(p.i), use(p.use)
{
++*use;//拷贝时计数器+1
}
HasPtrAsPtr& operator=(const HasPtrAsPtr&);
~HasPtrAsPtr();
private:
std::string *ps;
int i;
std::size_t *use; //用来记录有多少个对象共享*ps的成员
};
//HasPtrAsPtr.cpp
//拷贝赋值运算符执行类似拷贝构造函数和析构函数的工作
//1、它必须递增右侧运算对象的引用计数(即,拷贝构造函数)
//2、并递减左侧运算对象的引用计数,在必要时释放使用的内存(即,析构函数的工作)
HasPtrAsPtr & HasPtrAsPtr::operator=(const HasPtrAsPtr & rhs)
{
// TODO: 在此处插入 return 语句
++*rhs.use; //递增右侧运算对象的引用计数
if (--*use == 0)//递减本对象的引用计数
{
delete ps;//如果没有其他用户,释放本对象分配的成员
delete use;
}
ps = rhs.ps;//将数据从rhs拷贝到本对象
i = rhs.i;
use = rhs.use;
return *this;
}
HasPtrAsPtr::~HasPtrAsPtr()
{
if (--*use == 0)//如果引用计数变为0
{
delete ps;//释放string 内存
delete use;//释放计数器内存
}
}