C++句柄类(智能指针)小结

本文总结了C++中句柄类的作用,包括支持多态性和减少编译依赖。句柄类通过存储和管理基类指针,实现了动态行为和对象的正确销毁,防止内存泄露。同时介绍了引用计数的实现方式,以及如何通过泛型句柄类进一步提高灵活性。文章详细阐述了句柄类的赋值操作和使用,并对比了普通指针和句柄类的差异。

句柄类作用主要有两个:

  • 支持面向对象编程,实现多态性质;
  • 减少头文件的编译依赖关系,让文件间的编译更加独立。

句柄类存储和管理基类指针,指针既可以指向基类类型对象又可以指向派生类型对象。用户通过句柄类访问继承层次的操作,用户可以获得动态行为同时能够确保自动正确的销毁动态分配的对象,防止内存泄露。《C++ Primer》15.7节中用购物篮的例子说明了C++不能通过对象支持多态,而必须使用指针或引用。如果multiset保存基类的对象:派生类对象只有基类部分保存下来,而派生类部分被切掉;如果保存派生类的对象,基类对象无法有效转换为派生类对象。

Effective C++类的实现中条款22说明了为了实现接口和实现的分离,将对象的实现隐藏在指针身后,这样就能减少头文件的编译依赖关系。实现技术:使用引用数(referencecount)。句柄类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象共享同一指针。计数器变化的情况:

  • 创建类的新对象时,初始化指针并将引用计数置为1;
  • 对象作为另一对象的副本,拷贝构造函数拷贝指针并增加与之相应的引用计数;
  • 对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数;
  • 调用析构函数时,构造函数减少引用计数(如果引用计数减至0,则删除对象)。


一、指针型句柄

定义一个句柄类,管理继承层次中对象的指针,同时使用引用计数,自动释放相关继承类的对象。

//基类
class A{
public:
    ... //类定义
    virtual A *clone() const //虚函数,复制基类对象指针
    { return new A(*this); }
private:
    ... //类定义
};
//派生类
class B : public  A {
public:
    ... //类定义
    B *clone() const //重写,复制派生类对象指针
    { return new B(*this); }
private:
    ... //类定义
};

//句柄类,管理对象指针和引用计数
class C{
public:
    C() : p(0), use( new int(1)) {}

    // 用继承中的对象生成句柄类对象
    C( const A &a ) : p( a.clone()), use( new int(1)) {}
    //复制构造函数
    C( const Handle &h ) :
    p(h.p), use(h.use) { ++*use; } //计数加1
    ~C() { decr_use(); }
    const A *operator->() const  //返回对象指针
    { if(p) return p; }
    const A &operator*() const //返回对象
    { if(p) return *p; }
    C &operator=( const C &); //赋值
private:
    A *p; //对象指针
    int *use; //引用计数
void decr_use()
{
    if(--*use == 0)
    { delete p; delete use; }
}
};

赋值操作

C &C::operator=( const C &rhs )
{
    ++&rhs.use; //右操作数计数加1
    decr_use(); //检查左操作数计数
    p = rhs.p; //赋值
    use = rhs.use;
    return *this;
}

句柄类的使用


A a;
C h1(a); //用一个基类对象生成一个句柄类对象
C h2(h1); //用h1来初始化h2,它们对象指针指向同一个对象 a,计数指针指向同一个值。
B b;
C h3(b); //派生类对象生成一个句柄类对象
multiset<C> mc;
mc.insert(h1);
mc.insert(h2);
mc.insert(h3); //所有对象都能被完整保存
//注意:插入关联容器需要对象有比较函数

二、泛型句柄类

由于(一)中句柄类包含指向继承层次中某类型对象指针和计数式的复制控制,因此可以定义一个句柄类模版,管理指针和进行使用计数。

template <class T> class Handle {
public:
    // 默认构造函数
    Handle( T *p = 0 ): ptr(p), use( new int(1) ) { }
    // 复制构造函数
    Handle( const Handle& h ): ptr(h.ptr), use(h.use)
    { ++*use; }
    //重载操作符 * 和 -> ,const 和 非const版本
    T& operator*();
    T* operator->();
    const T& operator*() const;
    const T* operator->() const;
    // 重载操作符 =
    Handle& operator=( const Handle& );
    ~Handle() { rem_ref(); }
private:
    T* ptr; // 对象指针
    int *use; // 引用计数
    void rem_ref()
    {
         if (--*use == 0) 
        { 
            delete ptr; delete use; 
        } 
    }
};

基类A、派生类B和模版类中的其它函数的定义与(一)中,不再重复。

泛型句柄类使用

class C{
public:
    //默认构造函数
    C() : h() {}
    // 复制构造函数
    C( const A &a ) : h(a.clone()) {}
    //重载操作符 * 和 ->
    const A &operator*() const { return *h; }
    const A *operator->() const { return h.operator->(); }
private:
    Handle<A> h; // 使用句柄类
};

由于这个版本的类C没有指针成员,所以不需要复制控制成员,管理使用计数和相关对象的工作在 Handle 内部完成。

可以将使用模版句柄类定义的类C与原来的类C进行对比:两个类接口没有改变,因此类C的使用与上面相同。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值