范围参考成语的最佳实践?
Posted
技术标签:
【中文标题】范围参考成语的最佳实践?【英文标题】:Best Practice for Scoped Reference Idiom? 【发布时间】:2009-03-23 21:47:22 【问题描述】:我刚刚被一个错误所困扰,部分原因是我缺乏理解,部分原因是我认为我们的代码库中的设计欠佳。我很好奇如何改进我的 5 分钟解决方案。
我们正在使用引用计数的对象,在这些类的对象上我们有 AddRef() 和 Release()。一个特定的对象派生自 ref-count 对象,但获取这些对象实例的常用函数 (GetExisting) 将 AddRef() 隐藏在其自身内部,而不宣传它正在这样做。这需要在功能块的末尾执行 Release 以释放隐藏的 ref,但是没有检查 GetExisting() 实现的开发人员不会知道这一点,并且忘记在末尾添加 Release 的人该函数(例如,在修复错误的关键时刻)会泄漏对象。这当然是我的烧伤。
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()。是否有一些聪明的方法可以清理它以使其在语法上更漂亮,或者类似的东西会模糊容器的含义并为不熟悉代码的开发人员引入新问题?
谢谢。
【问题讨论】:
【参考方案1】:使用 boost::shared_ptr 可以定义自己的析构函数,例如我们在下一个示例中:http://www.boost.org/doc/libs/1_38_0/libs/smart_ptr/sp_techniques.html#com
【讨论】:
Mark Ransom 的回答是我想要的,但你的回答让我朝着学习新事物和整体问题更高水平解决方案的良好方向前进,所以我要标记它作为“The”的答案。谢谢。 很高兴听到。别客气。最好为所有类似的事情使用一个类型类,而不是为每个类编写单独的解决方案。【参考方案2】:是的,你可以为这个类实现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
作为智能参考。
【讨论】:
它在使用中是什么样的? 啊,我明白了。这会调用隐藏成员上的函数(这很有用,所以谢谢),但我想我想要的是一种更简洁的方法来传递隐藏成员 INTO 函数,也许通过将容器类别名为隐藏类。我想我可以重载 () 并让它返回 m_T... 不,那行不通。您需要从 ThreadContainer 到 Thread * 的自动转换,这是可能的,但可能会变得棘手。真的,如果您能够使用 Boost,我建议您查看下面 bb 的 Boost 建议。 shared_ptr 听起来正是您正在寻找的。span> Boost 的好处在于,它已经针对您想要的替代用途进行了大量调试......【参考方案3】:看看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
【讨论】:
【参考方案4】:我建议关注 bb advice 并使用 boost::shared_ptr。如果 boost 不是一个选项,您可以查看 std::auto_ptr,它很简单,可能满足您的大部分需求。考虑到 std::auto_ptr 具有您可能不想模仿的特殊移动语义。
该方法是同时提供 * 和 -> 运算符以及一个 getter(用于原始指针)和一个释放操作,以防您想要释放对内部对象的控制。
【讨论】:
【参考方案5】:您可以添加自动类型转换运算符来返回原始指针。 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!!!
【讨论】:
啊,这就是我想要的,我只是不知道该怎么做。以上是关于范围参考成语的最佳实践?的主要内容,如果未能解决你的问题,请参考以下文章