今天,我遇到了一个我认为非常普遍的问题,我不知道为什么直到现在才注意到它。假设您有一个要使用boost::shared_ptr<>
处理的类。此类的成员必须将自身的shared_ptr<>
转发给其他方法。换句话说,它必须为shared_ptr<>
指针创建一个this
。做到这一点的方法是将weak_ptr
保留在类中,并使用静态成员作为工厂对自己的实例进行new
,将其包装在shared_ptr<>
中,设置成员weak_ptr<>
并返回托管的shared_ptr<>
,每个被告知在boost::shared_ptr<>
中执行在线文档(http://www.boost.org/doc/libs/1_40_0/libs/smart_ptr/shared_ptr.htm#Introduction):
Because the implementation uses reference counting, cycles of shared_ptr instances will not be reclaimed. For example, if main() holds a shared_ptr to A, which directly or indirectly holds a shared_ptr back to A, A's use count will be 2. Destruction of the original shared_ptr will leave A dangling with a use count of 1. Use weak_ptr to "break cycles."
问题在于可以继承此类,而派生类对自己具有
weak_ptr<>
的要求相同。因此,我考虑了这种模式,我称其为“兔子洞”,以某种方式可以做一些等同于“覆盖成员的类型”的事情。
它通过让您想要“覆盖”模板
Sonetto::RabbitHole<>
内的继承类的成员来工作:class Base
{
protected:
Sonetto::RabbitHole<MemberBaseClass> mMember;
};
调用
mMember.get()
返回类型MemberBaseClass的引用以供使用。如果实例化了基类,则mMember.get()
将真正返回MemberBaseClass的实例。但是,让我们在方程式中添加另一个兔子洞级别,如下所示:class Derived : public Base
{
public:
Derived()
{
Base::mMember.link(mMember);
}
protected:
Sonetto::RabbitHoleLevel<MemberBaseClass,MemberDerivedClass> mMember;
};
这次我们使用模板
Sonetto::RabbitHoleLevel<>
。第一个模板参数是前一个兔子孔级别要求返回的类型,在这种情况下,它是MemberBaseClass
。第二个模板参数是如果Derived::mMember
是对象中最深的兔子洞级别,则将实际实例化的类型。显然,更深层的成员必须从该类型继承。构造函数负责如代码片段中所示的链接级别。当需要使用指针时,每个类都可以安全地使用自己想要的类型:void Base::doSomething()
{
MemberBaseClass &ref = mMember.get();
// [...]
}
void Derived::doSomething()
{
MemberDerivedClass &ref = mMember.get();
// [...]
}
// And so on...
我想问的是,人们可以帮助我找到哪些类型的继承方案使该模式无效。而且,这些的坏处是,每个兔子洞级别增加了12个字节的开销,因此,对于具有两个兔子洞和两个继承的类的类,将有72个字节的大开销。您是否认为这样的开销使该类成为在我的代码的许多地方使用的不好的类?代码如下:
// 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;
}
因此,我的问题的核心是:您个人认为此代码或模式本身有问题吗?如果是这样,我该怎么做才能改善它?您认为本来可以做得不同吗?
-第2次编辑-
我了解到,根据此处发布的答案并基于boost的文档,我无意间混淆了在该文档中阅读的内容。创建该
weak_ptr<>
的解决方案解决了从对象的构造函数中(实际上是从静态工厂方法中)为该指针选择shared_ptr<>
的问题。具有讽刺意味的是,我的方法甚至无法解决该问题,因为只能在完整对象构建之后才能访问Rabbit Hole :)我写了很多文本,解释了可以使用Rabbit Hole的另一种情况,但是在编写时,我得出的结论是,有一种更好的方法,具有更少的抽象,只需要将两个类分开即可。我的动机是,这两个类相互继承是很有意义的(比如说,整体上占60%),但是我认为将它们分开至少要占40%。将速度因子加到这40%,我猜想将它们分开就好了。我猜兔子洞将不得不等待另一个问题来解决,但我仍然喜欢它们。我敢肯定,在某些情况下使用它们会很有意义,而且,谢天谢地,我将确切地知道如何实现(实际上,我已经准备好了模板!(:)。稍后将只需要解决异常安全问题。如果我很快就这样做,那么我将编辑这篇文章,以便发现有用的模式的其他人可以选择使用它(顺便说一句,我在此声明代码段)发布公共(public) Realm ;不知道是否需要这样做,但我只会说,大声笑)。谢谢
最佳答案
似乎过于复杂;关于什么:
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());
}
};
Actor 阵容有点困惑,但是应该不成问题。除非对象的类型能够强制转换,否则它们不会被调用。
关于c++ - 我的Rabbit Hole模板有问题吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1501993/