C++中通过shared_ptr存储右值引用引发段错误的问题
问题分析与解决方案
核心错误:ostream运算符无限递归
你的段错误并非来自shared_ptr的无效引用,而是operator<<的无限递归调用:
std::ostream& operator<<(std::ostream& os, const Tensor& x) { std::cout << x << "\n"; // 这里会再次调用本运算符,无限递归 return os; }
这段代码会不断调用自身,导致栈溢出,表现为段错误。
修复步骤
1. 修复输出运算符
把输出逻辑改为直接打印data成员,避免递归:
std::ostream& operator<<(std::ostream& os, const Tensor& x) { os << x.data; return os; }
2. 完善Tensor的语义与运算符重载
当前的运算符只接受右值引用,限制了使用场景。可以调整运算符参数,让它同时支持左值和右值,同时确保临时对象的生命周期被shared_ptr正确接管:
修正后的完整代码:
#include <memory> #include <iostream> class Tensor; class Op{ public: std::shared_ptr<Tensor> lhs; std::shared_ptr<Tensor> rhs; Op(std::shared_ptr<Tensor> lhs) : lhs(lhs) {}; Op(std::shared_ptr<Tensor> lhs, std::shared_ptr<Tensor> rhs) : lhs(lhs) , rhs(rhs) {}; }; class Tensor{ public: std::shared_ptr<Op> op; int data; // 普通构造 Tensor(int y): data(y) {} // 移动构造 Tensor(Tensor&& lhs) noexcept : data(std::move(lhs.data)) , op(std::move(lhs.op)){} // 拷贝构造(可选,根据需求添加) Tensor(const Tensor& lhs) : data(lhs.data) , op(lhs.op){} }; // 修复后的输出运算符 std::ostream& operator<<(std::ostream& os, const Tensor& x) { os << x.data; return os; } // 调整运算符参数,支持左值和右值 Tensor operator*(Tensor lhs, Tensor rhs){ // 将传入的对象(可能是移动来的临时对象)转为shared_ptr std::shared_ptr<Tensor> lhs_ = std::make_shared<Tensor>(std::move(lhs)); std::shared_ptr<Tensor> rhs_ = std::make_shared<Tensor>(std::move(rhs)); Tensor ret(11); // 存储父节点引用 ret.op = std::make_shared<Op>(lhs_, rhs_); return ret; } int main() { Tensor a = Tensor(3) * Tensor(4); std::cout << a.data << "\n"; // 现在可以正常访问 std::cout << *a.op->lhs << "\n"; std::cout << *a.op->rhs << "\n"; return 0; }
关键说明
- 生命周期管理:通过
std::make_shared<Tensor>(std::move(lhs))将临时对象或左值对象移动到shared_ptr管理的内存中,确保这些对象不会被提前销毁,后续通过op->lhs/op->rhs访问时始终有效。 - 运算符灵活性:把运算符参数改为
Tensor lhs而非Tensor&& lhs,这样编译器会自动对左值调用拷贝构造,对右值调用移动构造,既支持链式调用(a+b)*(c+d),也支持左值相乘。 - 移动语义:保留移动构造函数,避免不必要的拷贝,提升性能。
内容的提问来源于stack exchange,提问作者Sean
相关产品推荐
相关产品推荐

