今天我遇到了一个我认为非常普遍的问题我自己想知道为什么我才注意到它。想象一下,您有一个要使用boost::shared_ptr<>
处理的课程。这个类的成员必须将自己的shared_ptr<>
转发给其他方法;换句话说,它必须为shared_ptr<>
指针创建this
s。这样做的方法是在类中保存weak_ptr
并使用静态成员作为new
自身实例的工厂,将其包装在shared_ptr<>
内,设置成员{ {1}}并返回托管weak_ptr<>
,按照shared_ptr<>
在线文档(http://www.boost.org/doc/libs/1_40_0/libs/smart_ptr/shared_ptr.htm#Introduction)进行操作:
因为实现使用引用计数,所以不会回收shared_ptr实例的循环。例如,如果main()将shared_ptr保存到A,它直接或间接地将shared_ptr保存回A,则A的使用计数将为2.原始shared_ptr的破坏将使A的悬空使用计数为1.使用weak_ptr “打破周期。”
问题在于这个类可以被继承,并且派生类具有对自己具有boost::shared_ptr<>
的相同要求。
所以我想到了这种模式,我称之为“兔子洞”,它可以在某种程度上做一些相当于“覆盖成员类型”的东西。
它的工作原理是让你想要在模板weak_ptr<>
内的继承类中“覆盖”的成员:
Sonetto::RabbitHole<>
调用class Base
{
protected:
Sonetto::RabbitHole<MemberBaseClass> mMember;
};
将返回MemberBaseClass类型的引用以供使用。如果实例化Base类,mMember.get()
将真正返回MemberBaseClass的实例。但是,让我们在等式中添加另一个兔子洞级别,如下所示:
mMember.get()
这次我们使用模板class Derived : public Base
{
public:
Derived()
{
Base::mMember.link(mMember);
}
protected:
Sonetto::RabbitHoleLevel<MemberBaseClass,MemberDerivedClass> mMember;
};
。第一个模板参数是前一个兔子洞级别需要返回的类型,在这种情况下,它是Sonetto::RabbitHoleLevel<>
。如果MemberBaseClass
是对象中最深的兔子洞级别,则第二个模板参数是实际实例化的类型。显然,更深层次的成员必须从这种类型继承。构造函数负责链接级别,如代码段中所示。当使用指针时,每个类都可以安全地使用自己想要的类型:
Derived::mMember
我想问的是,人们可以帮助我找到哪种类型的继承方案可能会使该模式无用。另外,关于这些的坏处是它为每个兔子洞级别增加了12个字节的开销,因此,对于有两个兔子洞和两个继承类的类,将有72字节的大开销。你是否认为这样的开销使得这个类在我的代码的许多地方都是一个坏类?代码如下:
void Base::doSomething()
{
MemberBaseClass &ref = mMember.get();
// [...]
}
void Derived::doSomething()
{
MemberDerivedClass &ref = mMember.get();
// [...]
}
// And so on...
所以,我的问题的核心是:你个人认为这个代码或模式本身有问题吗?如果是这样,我该怎么做才能改善它?你认为它可以以不同的方式完成吗?
- 第二次修改
据我所知,在这里发布的答案以及boost的文档的基础上,我无意中混淆了我在该文档中读到的内容。创建// SonettoRabbitHole.h
#ifndef SONETTO_RABBITHOLE_H
#define SONETTO_RABBITHOLE_H
#include <cstdlib>
namespace Sonetto
{
// ---------------------------------------------------------------------------------
// Sonetto::IHalfTypedRabbitHole declaration
// ---------------------------------------------------------------------------------
template <class B>
class IHalfTypedRabbitHole
{
public:
virtual ~IHalfTypedRabbitHole() {}
virtual B &get() = 0;
};
// ---------------------------------------------------------------------------------
// Sonetto::RabbitHoleLevel declaration
// ---------------------------------------------------------------------------------
template <class B,class T>
class RabbitHoleLevel : public IHalfTypedRabbitHole<B>
{
public:
inline RabbitHoleLevel();
virtual ~RabbitHoleLevel();
template <class _T,class D>
void link(RabbitHoleLevel<_T,D> &link)
{
mNextLevel = &link;
}
inline virtual T &get();
private:
IHalfTypedRabbitHole<T> *mNextLevel;
T *mImpl;
};
// ---------------------------------------------------------------------------------
// Sonetto::RabbitHole declaration
// ---------------------------------------------------------------------------------
template <class T>
struct RabbitHole : public RabbitHoleLevel<T,T> {};
// ---------------------------------------------------------------------------------
// Sonetto::RabbitHoleLevel template and inline implementations
// ---------------------------------------------------------------------------------
template <class B,class T>
inline RabbitHoleLevel<B,T>::RabbitHoleLevel()
: mNextLevel(NULL), mImpl(NULL) {}
// ---------------------------------------------------------------------------------
template <class B,class T>
RabbitHoleLevel<B,T>::~RabbitHoleLevel()
{
// If we are at the end of the rabbit hole, we
// delete what we have instantiated here
if (mNextLevel == NULL)
{
delete mImpl;
}
}
// ---------------------------------------------------------------------------------
template <class B,class T>
inline T &RabbitHoleLevel<B,T>::get()
{
if (mImpl == NULL)
{
if (mNextLevel)
{
mImpl = &mNextLevel->get();
}
else
{
mImpl = new T();
}
}
return *mImpl;
}
// ---------------------------------------------------------------------------------
}
#endif
// main.cpp to demonstrate usage
#include <iostream>
#include "SonettoRabbitHole.h"
// "Base 'virtual' member" declaration
class MemberReality
{
public:
// This will be called when you call an instantiated
// Reality's callme() method
virtual void callme() const { std::cout << "Member Reality.\n"; }
};
// "Overriden 'virtual' member" declaration
class MemberWonderland : public MemberReality
{
public:
// This will be called when you call an instantiated
// Wonderland's Reality::callme() method
void callme() const { std::cout << "Member Wonderland.\n"; }
};
// Base class with RabbitHole
// Classes inheriting others with rabbit holes can override those holes
// by linking their ones' with the hole of the first class it inherits that
// expresses a level of this hole in question in their constructors; see
// class Wonderland below
class Reality
{
public:
void callme() // Notice this isn't virtual, but it calls
// MemberWonderland in this example: an "overriden member"
{
std::cout << "Calling from reality...\n";
// Access to the deepest hole is granted using the class'
// rabbit hole member; the first time mRabbitHole.get() is called,
// the its pointer is instantiated as its deepest linked type
mRabbitHole.get().callme();
}
protected:
Sonetto::RabbitHole<MemberReality> mRabbitHole;
};
// Derived class extending base's rabbit hole
class Wonderland : public Reality
{
public:
Wonderland()
{
// Link previous rabbit hole level with this level
// Keep in mind that this very Wonderland class could
// be inherited, so it would be wrong to call
// mRabbitHole.get() from this constructor, as it would
// instantiate MemberWonderland when it should have
// instantiated the class from the next rabbit hole level
// Because of that, as a rule, you can only access the
// rabbit hole from a constructor if you are plain sure it
// will be the deepest level of the hole
// Rabbit holes work by delaying construction of the objects
// to the moment they're needed for use (lazy initialization)
Reality::mRabbitHole.link(mRabbitHole);
}
protected:
// The first template parameter is the base pointer type
// For linkages to work, it must be the same type as the second template
// parameter passed in the previous level's template
Sonetto::RabbitHoleLevel<MemberReality,MemberWonderland> mRabbitHole;
};
int main()
{
Reality r;
Wonderland w;
r.callme(); // Prints: 'Member Reality.'
std::cout << '\n';
w.callme(); // Prints: 'Member Wonderland.'
return 0;
}
的解决方案解决了从对象的构造函数中选择weak_ptr<>
到this指针的问题(实际上,来自静态工厂方法)。具有讽刺意味的是,我的方法甚至没有解决这个问题,因为兔子洞只能在完整的对象构建后才能访问:)我写了很多文字解释另一个案例,我可以使用兔子洞,但写作时我得出结论,有一个更好的方法,抽象更少,只需要分开两个类。我的动机是,这两个类很有意义(总体来说,60%)是继承的,一个是另一个,但我认为它至少有40%将它们分开。将速度因子添加到40%,我猜它可以将它们分开。我猜兔子洞将不得不等待另一个问题来解决,但我仍然喜欢它们。我很确定会有这样的情况,他们会很有意义地使用它们,而且,谢天谢地,我将确切地知道如何实现(事实上,我将为它准备好模板!(:)。我我只需要稍后解决它中的异常安全问题。如果我很快就这样做了,我会编辑这篇文章,以便找到有用模式的其他人可以选择它可以使用(顺便说一下,我在此声明代码片段)邮政公共领域;不知道是否需要,但我会说,哈哈)。
由于
答案 0 :(得分:4)
似乎过于复杂;怎么样:
class Base : public enable_shared_from_this<Base> {
private:
// to get a shared ptr, call shared_from_this()
};
class Derived : public Base {
private:
shared_ptr<Derived> shared_from_this() {
return static_pointer_cast<Derived>(Base::shared_from_this());
}
shared_ptr<Derived const> shared_from_this() const {
return static_pointer_cast<Derived const>(Base::shared_from_this());
}
};
演员们有点乱,但他们应该不是问题;除非对象的类型使得演员成功,否则他们不会被调用。
答案 1 :(得分:3)
我可能误解了你的问题,但你看过enable_shared_from_this
了吗?