提前声明 ,这是一篇水博!!!
1.使用智能指针实现:
#include <string>
#include <iostream>
#include <memory>
using namespace std ;
class HasPtr {
public:
HasPtr(const std::string &s = std::string()) : ps(make_shared<string>(s)), i(0) { }
HasPtr(const HasPtr& hp) : ps(hp.ps), i(hp.i) {
cout << " 拷 贝 构 造 函 数 " << endl ; // 又多了一个对象使用 *ps
}
HasPtr& operator=(const HasPtr &rhs){
cout << " 赋 值 运 算 符 " << endl ;
ps = rhs.ps ;
i = rhs.i ;
return *this;
}
~HasPtr(){ }
void print(){
cout << "*ps == " <<*ps << endl ;
cout << "i == " << i << endl ;
cout << "ps 引用计数 == " <<ps.use_count() << endl << endl << endl;
}
private:
shared_ptr<string> ps ; //定义智能指针
int i ; // 额外数据成员
};
int main(void){
HasPtr test1("liushengxi") ,test3("3333333") ;
HasPtr test2(test1);
test1.print(); // 引用计数应该 == 2
test2.print(); // 引用计数应该 == 2
test3 = test1 ; //对于这种情况,左边引用计数 -1 ,右边引用计数 +1
test1.print() ; //引用计数应该 == 3
test3.print() ; // 引用计数应该 == 3 ,
//而此时test3 初始化时指向的字符串“33333” ,应该已经被销毁了,这是因为test3 的引用计数 == 0 了,智能指针自动销毁
return 0 ;
}
运行结果:
2. 自己定义引用计数实现:
#include <string>
#include <iostream>
#include <memory>
using namespace std ;
class HasPtr {
public:
HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0),use(new size_t(1)) { }
HasPtr(const HasPtr& hp) : ps(new std::string(*hp.ps)), i(hp.i) ,use(hp.use) {
cout << " 拷 贝 构 造 函 数 " << endl ; //又多了一个对象使用 *ps ,use++ ;
++(*hp.use) ;
}
HasPtr& operator=(const HasPtr &rhs){
cout << "赋值运算符 " << endl ;
++*rhs.use ;
--*use ;
auto temp_ps = new string( *rhs.ps) ;
delete ps ;
ps = temp_ps ;
i = rhs.i ;
use = rhs.use ;
return *this;
}
~HasPtr(){
--(*use) ;
if( *use == 0 )
delete ps ,delete use ;
}
void print(){
cout << *ps << endl ;
cout << i << endl ;
cout << *use << endl << endl << endl ;
}
private:
std::string *ps ;
int i ;
size_t *use ; //记录有多少个对象共享 *ps 成员
};
int main(void){
HasPtr test1("liushengxi") ,test3("3333333") ;
HasPtr test2(test1);
test1.print(); // 2
test2.print(); // 2
test3 = test1 ;
test1.print() ; // 3
test3.print() ;
return 0 ;
}
运行结果:
最后说一下什么叫行为像指针的类?什么叫行为像值的类?
1.首先解释行为像值,对于行为像值,实际上就是对于每一个已经实例化的类的对象,对于每一个类管理的资源(这里C++primer中是针对于可以动态分配内存的容器而言的,实际上应该所有非内置类型需要动态申请的类型都需要这么做),当对象与对象之间进行复制操作的时候,每个对象都保存有一份副本,使得当有一个对象中对应的成员占有的空间被释放的时候,对应赋值过的对象中的相应成员不受影响。实际上这样释放空间的过程不应该仅仅存在于析构函数中,在我们重载赋值操作符(=)的时候也应该有释放被赋值的对象原来占有的类外空间的步骤,每一个实例化的对象中都要存储一份对应的成员的话,就不能像行为像指针的类中赋值操作的那样直接指针等于指针的方式,因为在行为像值的类中是有专门的计数器的,而在行为像值的类中是没有的,所以不能用同样的指针,必须要开辟新的地址。
c++ primer 中的一个小例子
#include <string>
#include <iostream>
#include <memory>
using namespace std ;
class HasPtr {
public:
HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { }
HasPtr(const HasPtr& hp) : ps(new std::string(*hp.ps)), i(hp.i) {
cout << " 拷 贝 构 造 函 数 " << endl ;
}
HasPtr& operator=(const HasPtr &rhs){
cout << "赋值运算符 " << endl ;
auto temp_ps = new string( *rhs.ps) ;
delete ps ;
ps = temp_ps ;
i = rhs.i ;
return *this;
}
~HasPtr(){
delete ps ;
}
void print(){
cout << *ps << endl ;
cout << i << endl ;
}
private:
std::string *ps ;
int i ;
};
int main(void){
HasPtr test1("liushengxi") ,test3("zuishuai");
HasPtr test2(test1);
test2.print();
test2 = test2 ;
test2.print();
}
需要说明的一点是:编写赋值运算符时的编码顺序一定是这个样子的(这样可以很优雅的解决对象赋予它自身的问题)
1.将右侧对象拷贝拷贝到一个局部临时对象中
2.销毁左侧运算对象现有成员
3.将临时局部对象拷贝到左侧运算对象的成员
2.对于想要使类的行为像指针的话,就需要加入新的计数机制,解决的问题就是如果类中有上述所说的默认析构函数不能释放的资源的时候需要程序员自己决定什么时候进行资源的释放。那么这个时候的机制就有点类似于智能指针了,我们需要在类中加入一个私有成员用于引用计数。