类的行为:
- 像值
- 像指针
- 类的行为像值,意味着它应该也有自己的状态。当我们拷贝一个像值的对象时,副本和原对象时完全独立的。改变副本不会对原对象有任何影响,反之亦然。
- 行为像指针的类则共享状态。当我们拷贝一个这种类的对象时,副本和原对象使用相同的底层数据。改变副本也会改变原对象,反之亦然。
行为像值的类
//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;//释放计数器内存 } }