智能指针
某些时候会在一个类里定义一个指向另一个类的指针,如此多个对象可通过指针访问一个共享对象,但如果指向的对象被删除,则可能产生一批悬垂指针。解决这个问题又实现共享可使用智能指针类。
动态分配一个共享对象并把它的地址传给智能指针类的对象,类的用户可通过指针访问共享对象,但不能删除其指针。删除指针的行为由智能指针类管理,在撤销最后一个智能指针类的对象时删除指向共享对象的指针。
智能指针类可通过引用计数实现,类除了有一个指针成员,还维护一个计数,该计数说明当前有多少个对象指向共享对象。
计数器不能直接放在智能指针类中,否则无法更新之前的智能指针对象。如下情况:
int obj;
HasPtr p1(&obj, 42);
HasPtr p2(p1); // p1 and p2 both point to same int object
HasPtr p3(p1); // p1, p2, and p3 all point to same int object
HasPtr为智能指针类,如计数器存在HasPtr对象中,则创建p3时无法更新p2中的计数。
可用计数器类来解决这个问题:
U_Ptr是一个计数器类。HasPtr为其友元,可访问ip,use。
class U_Ptr {
friend class HasPtr;
int *ip;
size_t use;
U_Ptr(int *p): ip(p), use(1) { }
~U_Ptr() { delete ip; }
};
计数器类的使用:
HasPtr构造函数接受一个共享对象的指针并新建一个计数器类,同时将指针传递给计数器类,并初始化计数为1。复制构造增加计数,析构函数在计数为0里删除计数器类。
class HasPtr {
public:
HasPtr(int *p, int i): ptr(new U_Ptr(p)), val(i) { }
HasPtr(const HasPtr &orig):
ptr(orig.ptr), val(orig.val) { ++ptr->use; }
HasPtr& operator=(const HasPtr&);
~HasPtr() { if (--ptr->use == 0) delete ptr; }
private:
U_Ptr *ptr; // points to use-counted U_Ptr class
int val;
};
赋值操作比较复杂,需要先将右操作数的计数加1,再将左操作数的计数减1,防止自身赋值时删除自身。
HasPtr& HasPtr::operator=(const HasPtr &rhs){
++rhs.ptr->use; // increment use count on rhs first
if (--ptr->use == 0) delete ptr; // if use count goes to 0 on this object, delete it
ptr = rhs.ptr; // copy the U_Ptr object
val = rhs.val; // copy the int member
return *this;
}
句柄类
具有多层次继承关系的几个类的对象无法保存在一个容器中,因为器只保存一种类型。可以在容器中保存对象的指针解决,但必须保证容器存在时对象存在,而动态分配的对象消失时要释放对象。
更好的解决策略是使用句柄类。
Sales_item为句柄类,可将其绑定到Item_base类。
Sales_item item(Bulk_item("0-201-82470-1", 35, 3, .20));
item->net_price(); // virtual call to net_price function
默认构造初始化计数,第二个构造接受一个基类对象的引用,复制构造接受句柄类对象引用并将引用计数加1。析构调用decr_use()在引用计数为0时删除绑定对象的指针。箭头->重载函数返回绑定的对象的指针,解引用*重载函数返回绑定对象的引用。
class Sales_item {
public:
Sales_item(): p(0), use(new std::size_t(1)) { }
Sales_item(const Item_base&);
Sales_item(const Sales_item &i): p(i.p), use(i.use) { ++*use; }
~Sales_item() { decr_use(); }
Sales_item& operator=(const Sales_item&);
const Item_base *operator->() const { if (p) return p;
else throw std::logic_error("unbound Sales_item");
}
const Item_base &operator*() const { if (p) return *p;
else throw std::logic_error("unbound Sales_item"); }
private:
Item_base *p; // pointer to shared item
std::size_t *use; // pointer to shared use count
void decr_use() { if (--*use == 0) { delete p; delete use; } }
};
同前面的智能指针类,在赋值=重载函数中应该先将右操作数的引用计数加1再做其余操作。
Sales_item&
Sales_item::operator=(const Sales_item &rhs){
++*rhs.use;
decr_use();
p = rhs.p;
use = rhs.use;
return *this;
}
要支持句柄类须从基类到最后一个派生类定义一个clone虚函数返回当前对象副本
class Item_base {
public:
virtual Item_base* clone() const { return new Item_base(*this); }
};
class Bulk_item : public Item_base {
public:
Bulk_item* clone() const { return new Bulk_item(*this); }
};
用clone虚函数构造句柄对象,这样句柄类则可以绑定所有继承层次的对象。
Sales_item::Sales_item(const Item_base &item): p(item.clone()), use(new std::size_t(1)) { }
将句柄类用在容器中还须为容器算法提供一个用于比较的函数。将Comp定义为函数类型指针的别名,在第二行中指定其使用的比较函数为Comp这种带两个Sales_item引用参数返回bool的类型,并将函数compare绑定到容器中。
typedef bool (*Comp)(const Sales_item&, const Sales_item&);
std::multiset<Sales_item, Comp> items(compare);
inline bool compare(const Sales_item &lhs, const Sales_item &rhs){
return lhs->book() < rhs->book();
}
注:以上代码出自《c++primer》
原文链接: https://www.cnblogs.com/zhsidai/p/3222074.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/97677
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!