shared_ptr向纯虚基类的拷贝转换机制及实例场景解析
核心问题拆解
你提到的场景里有两个关键疑问,咱们逐个说清楚:
1. shared_ptr如何完成从A到IError的转换?
std::shared_ptr本身支持隐式向上转型,只要派生类(A)是基类(IError)的可访问派生类(这里是public继承,完全符合要求)。
这个转换依赖于shared_ptr的模板构造函数,而非普通的类型强制转换。标准库中shared_ptr提供了这样的模板构造:
template<class Y> shared_ptr(const shared_ptr<Y>& r) noexcept;
当你把shared_ptr<A>传给需要shared_ptr<IError>的B构造函数时,编译器会自动匹配这个模板构造函数,检查A*是否能隐式转换为IError*(因为A继承自IError,这个转换合法),然后创建一个指向同一个对象的shared_ptr<IError>实例。
2. 这是不是普通的拷贝构造调用?
不是。
普通的shared_ptr拷贝构造函数是针对同类型的:
shared_ptr(const shared_ptr& r) noexcept;
而咱们场景中是从shared_ptr<A>转成shared_ptr<IError>,属于不同类型之间的构造,调用的是上面提到的模板构造函数。
不过要注意:不管是拷贝构造还是模板构造,最终的效果都是共享同一个控制块,引用计数会正常增加。比如你的代码里:
objA创建时,引用计数是1;- 传给B的构造函数后,
mErrorReporter(shared_ptr<IError>)和objA共享同一个控制块,引用计数变成2; - 当
objA和objB都被销毁时,引用计数降到0,才会释放A的实例。
纯虚基类IError的影响?
IError是纯虚基类这件事,对shared_ptr的转换逻辑没有任何特殊影响。纯虚只是限制了不能直接实例化IError,但shared_ptr<IError>完全可以指向它的派生类实例(比如A),这和普通非纯虚基类的情况完全一致。
你的示例代码能正常运行,就是因为shared_ptr<IError>可以通过模板构造函数从shared_ptr<A>隐式转换而来,然后调用OnError()时,通过多态调用A的实现。
修正后的示例代码
#include <iostream> #include <string> #include <memory> // 原代码遗漏了shared_ptr依赖的头文件 class IError { public: virtual ~IError(){}; virtual void OnError() = 0; }; class A : public IError { public: A(){}; void OnError(){std::cout << "Error Occured" << std::endl;} }; class B { public: B(std::shared_ptr<IError> errorReporter): mErrorReporter(errorReporter){} void Action(){mErrorReporter->OnError();} private: std::shared_ptr<IError> mErrorReporter; }; int main() { auto objA = std::make_shared<A>(); auto objB = std::make_shared<B>(objA); objB->Action(); }
内容的提问来源于stack exchange,提问作者TruckerCat

