c++智能指针,句柄类

智能指针

某些时候会在一个类里定义一个指向另一个类的指针,如此多个对象可通过指针访问一个共享对象,但如果指向的对象被删除,则可能产生一批悬垂指针。解决这个问题又实现共享可使用智能指针类。

动态分配一个共享对象并把它的地址传给智能指针类的对象,类的用户可通过指针访问共享对象,但不能删除其指针。删除指针的行为由智能指针类管理,在撤销最后一个智能指针类的对象时删除指向共享对象的指针。

智能指针类可通过引用计数实现,类除了有一个指针成员,还维护一个计数,该计数说明当前有多少个对象指向共享对象。

计数器不能直接放在智能指针类中,否则无法更新之前的智能指针对象。如下情况:

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为其友元,可访问ipuse

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

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月10日 上午4:23
下一篇 2023年2月10日 上午4:23

相关推荐