如何在派生类中保留shared_ptr并传递右值给基类构造函数?
问题描述
我有一个小型类继承体系,需要将std::shared_ptr传递给接受右值的基类构造函数,同时希望在派生类中保留该std::shared_ptr。简化后的问题代码如下:
class Base { public: Base(std::shared_ptr<int>&& p) : a_{p} { } private: std::shared_ptr<int> a_; }; class Derived : public Base { public: Derived(std::shared_ptr<int> p) : Base(copy_shared_ptr(p)) // 此写法不合法,希望实现类似逻辑 { } private: std::shared_ptr<int> copy_shared_ptr(std::shared_ptr<int> p) { p_ = p; // 会触发段错误,因为p_尚未初始化 std::cout << "Now p_ is " << p_.use_count() << std::endl; return p_; } std::shared_ptr<int> p_; }; int main() { Derived d{std::make_shared<int>(5)}; }
问题核心在于:基类构造函数会优先于派生类成员变量执行,因此无法在传递shared_ptr给基类前先初始化派生类的p_,直接赋值未初始化的p_会触发未定义行为(段错误)。
解决方案
方法一:复制+移动,兼顾效率与需求
利用std::shared_ptr的拷贝特性,先复制一份给基类作为右值传递,再将原构造函数参数移动给派生类成员,避免不必要的拷贝开销:
#include <memory> #include <iostream> class Base { public: Base(std::shared_ptr<int>&& p) : a_{std::move(p)} // 用std::move转移临时右值所有权,避免基类构造时的拷贝 { } private: std::shared_ptr<int> a_; }; class Derived : public Base { public: // 参数使用值传递,方便后续移动操作 Derived(std::shared_ptr<int> p) : Base(std::shared_ptr<int>(p)), // 复制p生成临时右值,传递给基类 p_(std::move(p)) // 将原参数的所有权移动给p_,无额外拷贝 { std::cout << "Derived p_ use count: " << p_.use_count() << std::endl; } private: std::shared_ptr<int> p_; }; int main() { Derived d{std::make_shared<int>(5)}; }
方法二:委托构造优先初始化派生类成员
如果想优化拷贝次数,可以通过私有委托构造函数先初始化派生类成员,再基于成员复制出右值传递给基类:
#include <memory> #include <iostream> class Base { public: Base(std::shared_ptr<int>&& p) : a_{std::move(p)} { } private: std::shared_ptr<int> a_; }; class Derived : public Base { private: // 私有委托构造:先初始化p_,再给基类传值 Derived(std::shared_ptr<int> p, int) : p_(std::move(p)), Base(std::shared_ptr<int>(p_)) // 复制p_生成右值传给基类 { std::cout << "Derived p_ use count: " << p_.use_count() << std::endl; } public: Derived(std::shared_ptr<int> p) : Derived(std::move(p), 0) // 委托给私有构造函数 { } private: std::shared_ptr<int> p_; }; int main() { Derived d{std::make_shared<int>(5)}; }
关键说明
- 原代码的错误根源是在派生类成员初始化前对其赋值,这属于未定义行为,必须避免。
- 值传递的构造函数参数是传入对象的副本,我们可以安全地将其移动给派生类成员,同时复制一份给基类使用。
- 基类构造中使用
std::move(p)能直接转移临时右值的所有权,减少引用计数的操作开销。
内容的提问来源于stack exchange,提问作者SupAl
相关产品推荐
相关产品推荐

