在C++中,智能指针是一种自动管理内存分配和释放的工具,其中std::shared_ptr
是使用最广泛的智能指针之一。它通过引用计数机制来管理对象的生命周期,确保对象在没有引用时自动销毁。然而,当需要在类的成员函数中获取当前对象的shared_ptr
时,直接使用裸指针(raw pointer)传递this
指针会破坏智能指针的使用规则,从而失去其优势。为了解决这个问题,C++11引入了std::enable_shared_from_this
模板类,它允许在类的成员函数中安全地获取当前对象的shared_ptr
。
std::enable_shared_from_this
类通过一个成员weak_ptr
来持有当前对象的弱引用,并提供了一个shared_from_this
方法来返回当前对象的shared_ptr
。这个weak_ptr
是在shared_ptr
的构造函数中初始化的,通过一个巧妙的技巧静态地判断被包装的对象是否继承自enable_shared_from_this
。
但是,如果一个对象的继承树中出现了多个enable_shared_from_this
,就会出现问题。在这种情况下,编译器的行为可能会有所不同,导致只有一个或没有weak_ptr
被初始化。这意味着,即使通过shared_ptr
实例化了类,尝试访问shared_from_this
也可能导致段错误。
本文提出了一种解决方案,可以初始化继承树中所有的enable_shared_from_this
的weak_ptr
。首先,需要稍微修改一下enable_shared_from_this
,创建一个新的类EnableSharedFromThis
。然后,让类继承自EnableSharedFromThis
而不是enable_shared_from_this
。
接下来,创建了一个名为SmartPtrBuilder
的类,它包含一个静态模板方法CreateSharedPtr
,可以调用这个方法,指定一个变量数量的模板参数,每个参数对应一个需要初始化的EnableSharedFromThis
继承的类。
此外,还提供了一个工厂方法,这样用户就不需要使用SmartPtrBuilder::CreateSharedPtr
来实例化类,从而不需要在任何地方正确指定模板参数的类型。可以直接提供一个工厂方法来隐藏所有这些细节。
另一种解决方案是使用dynamic_cast
,但这可能会对性能产生影响,并且需要RTTI(运行时类型信息),这在某些环境中(如某些实时操作系统)可能不可用。此外,如果使用虚拟继承,static_cast
可能无法编译,这就是为什么被迫使用dynamic_cast
作为通用解决方案。
如果能够静态地(在编译时)探索或检查继承树,那么就可以不需要在构建shared_ptr
时提供所有继承自enable_shared_from_this
的类。尽管在理论上可以在C++编译器中实现这个特性,但认为在目前的C++标准(写作本文时为C++11)中还没有实现。
最后,还创建了一个变长模板版本的SmartPtrBuilder
,如果需要,请在评论中询问或联系。
// EnableSharedFromThis 类的实现
template <typename T>
class EnableSharedFromThis {
public:
EnableSharedFromThis() : m_weakPtr() {}
std::shared_ptr<T> shared_from_this() {
return std::shared_ptr<T>(m_weakPtr.lock());
}
protected:
std::weak_ptr<T> m_weakPtr;
};
// SmartPtrBuilder 类的实现
template <typename T, typename... Args>
std::shared_ptr<T> CreateSharedPtr(Args... args) {
return std::shared_ptr<T>(new T(args...), [](T* p) {
delete p;
});
}
// 使用示例
class Base : public EnableSharedFromThis<Base> {
public:
Base(int iValue) : mValue(iValue) {}
void BaseFunc() {
auto sharedPtr = shared_from_this();
FuncBase(sharedPtr);
}
void PrintValue() {
std::cout << "Value = " << mValue << std::endl;
}
private:
int mValue;
};
class Derived : public Base, public EnableSharedFromThis<Derived> {
public:
Derived(int iValue) : Base(iValue) {}
void DerivedFunc() {
auto sharedPtr = shared_from_this();
FuncDerived(sharedPtr);
}
};
void FuncBase(std::shared_ptr<Base> iBase) {
iBase->PrintValue();
}
void FuncDerived(std::shared_ptr<Derived> iDerived) {
iDerived->PrintValue();
}
$ g++ -std=c++0x main.cpp -o esft
$ ./esft