Scoped Reference Idiom的最佳实践?

时间:2009-03-23 21:47:23

标签: c++ pointers

我刚被一个错误所困扰,部分原因在于我缺乏理解,部分原因是我认为我们的代码库中的设计欠佳。我很好奇我的5分钟解决方案是如何改进的。

我们正在使用引用计数的对象,我们在这些类的对象上有AddRef()和Release()。一个特定的对象是从ref-count对象派生的,但是获取这些对象的实例(GetExisting)的常用函数会在其自身内部隐藏AddRef()而不会通知它正在这样做。这需要在功能块的末尾执行Release以释放隐藏的ref,但是没有检查GetExisting()的实现的开发人员不会知道这一点,并且忘记在最后添加Release的人这个函数(比如说,在一个疯狂的bug修复关键时刻)泄漏了对象。当然,这是我的烧伤。


void SomeFunction(ProgramStateInfo *P)
{
   ThreadClass *thread = ThreadClass::GetExisting( P );
   // some code goes here
   bool result = UseThreadSomehow(thread);
   // some code goes here
   thread->Release();  // Need to do this because GetExisting() calls AddRef()
}

所以我写了一个小课程,以避免在这些函数结束时需要Release()。


class ThreadContainer
{
private:
    ThreadClass *m_T;
public:
    ThreadContainer(Thread *T){ m_T = T; }
    ~ThreadContainer() { if(m_T) m_T->Release(); }
    ThreadClass * Thread() const { return m_T; }
};

现在我可以这样做:


void SomeFunction(ProgramStateInfo *P)
{
   ThreadContainer ThreadC(ThreadClass::GetExisting( P ));
   // some code goes here
   bool result = UseThreadSomehow(ThreadC.Thread());
   // some code goes here
   // Automagic Release() in ThreadC Destructor!!!
}

我不喜欢的是访问线程指针,我必须调用ThreadContainer的成员函数Thread()。是否有一些聪明的方法可以清理它以使它在语法上更漂亮,或者是否会使容器的含义变得模糊不清并为不熟悉代码的开发人员引入新问题?

感谢。

5 个答案:

答案 0 :(得分:10)

使用boost :: shared_ptr 可以定义自己的析构函数,例如我们在下一个示例中:http://www.boost.org/doc/libs/1_38_0/libs/smart_ptr/sp_techniques.html#com

答案 1 :(得分:7)

是的,您可以为该类实现operator ->(),无论您返回什么内容,都会递归调用operator ->()

class ThreadContainer
{
private:
    ThreadClass *m_T;
public:
    ThreadContainer(Thread *T){ m_T = T; }
    ~ThreadContainer() { if(m_T) m_T->Release(); }
    ThreadClass * operator -> () const { return m_T; }
};

它有效地为你的包装类使用智能指针语义:

Thread *t =  new Thread();
...
ThreadContainer tc(t);
...
tc->SomeThreadFunction(); // invokes tc->t->SomeThreadFunction() behind the scenes...

您还可以编写转换函数,以类似的方式启用UseThreadSomehow(ThreadContainer tc)类型调用。

如果选择Boost,我认为您也可以设置shared_ptr作为智能参考。

答案 2 :(得分:4)

看看ScopeGuard。它允许这样的语法(从该链接无耻地窃取):

{
    FILE* topSecret = fopen("cia.txt");
    ON_BLOCK_EXIT(std::fclose, topSecret);
    ... use topSecret ...
} // topSecret automagically closed

或者您可以尝试Boost::ScopeExit

void World::addPerson(Person const& aPerson) {
    bool commit = false;
    m_persons.push_back(aPerson);  // (1) direct action
    BOOST_SCOPE_EXIT( (&commit)(&m_persons) )
    {
        if(!commit)
            m_persons.pop_back(); // (2) rollback action
    } BOOST_SCOPE_EXIT_END

    // ...                        // (3) other operations

    commit = true;                // (4) turn all rollback actions into no-op
}

答案 3 :(得分:2)

我建议关注bb advice并使用boost :: shared_ptr<&gt ;.如果不能选择boost,你可以查看std :: auto_ptr<>,这很简单,可能满足你的大部分需求。考虑到std :: auto_ptr具有您可能不想模仿的特殊移动语义。

该方法提供*和 - >运算符和getter(用于原始指针)和释放操作,以防您想要释放对内部对象的控制。

答案 4 :(得分:1)

您可以添加自动类型转换运算符以返回原始指针。 Microsoft的CString类使用这种方法来轻松访问底层字符缓冲区,我总是觉得它很方便。使用此方法可能会发现一些令人不快的意外,因为在任何时候您都有隐式转换,但我没有遇到过任何意外。

class ThreadContainer
{
private:
    ThreadClass *m_T;
public:
    ThreadContainer(Thread *T){ m_T = T; }
    ~ThreadContainer() { if(m_T) m_T->Release(); }
    operator ThreadClass *() const { return m_T; }
};

void SomeFunction(ProgramStateInfo *P)
{
   ThreadContainer ThreadC(ThreadClass::GetExisting( P ));
   // some code goes here
   bool result = UseThreadSomehow(ThreadC);
   // some code goes here
   // Automagic Release() in ThreadC Destructor!!!
}