C++实现n叉树变更访问者模式时shared_ptr相关问题咨询
解决方案
你遇到的核心问题是如何从已被std::shared_ptr管理的对象的this裸指针,获取对应引用计数正确的智能指针,C++标准库提供的std::enable_shared_from_this是专门处理这类场景的原生方案,无需额外封装持有指针的类。
核心修改步骤
- 让
BaseExpr继承std::enable_shared_from_this<BaseExpr>,获得从this创建shared_ptr的能力 - 调整
Visit方法的入参为std::shared_ptr<const T>,避免裸指针构建智能指针的非法操作 - 在
Mutate方法中调用shared_from_this()获取当前节点的智能指针,传递给访问者
修改后的完整代码
#include <memory> #include <string> // Forward declarations class ExpressionTreeMutator; struct BaseExpr; enum class ExpressionType { kString, kInt, }; using ExprPtr = std::shared_ptr<BaseExpr>; // 继承enable_shared_from_this struct BaseExpr : public std::enable_shared_from_this<BaseExpr> { explicit BaseExpr(ExpressionType expression_type) : expression_type_(expression_type) {} virtual ~BaseExpr() = default; virtual ExprPtr Mutate(ExpressionTreeMutator* visitor) const = 0; ExpressionType expression_type_; }; template<typename T> struct Expr : public BaseExpr { explicit Expr(ExpressionType expression_type) : BaseExpr(expression_type) {} ~Expr() override = default; ExprPtr Mutate(ExpressionTreeMutator* visitor) const override; }; struct String : public Expr<String> { // 新增静态创建方法,确保所有实例都由shared_ptr管理 static std::shared_ptr<String> Make(std::string value) { return std::make_shared<String>(std::move(value)); } explicit String(std::string value) : Expr(ExpressionType::kString), value_(std::move(value)) {} std::string value_; }; struct Int : public Expr<Int> { static std::shared_ptr<Int> Make(int64_t value) { return std::make_shared<Int>(value); } explicit Int(int64_t value) : Expr(ExpressionType::kInt), value_(value) {} int64_t value_; }; class ExpressionTreeMutator { public: virtual ~ExpressionTreeMutator() = default; protected: template<typename T> friend struct Expr; // 入参改为shared_ptr<const T> virtual ExprPtr Visit(std::shared_ptr<const Int> expr) { // 无需修改直接返回原节点 bool need_modify = false; if (!need_modify) { return std::const_pointer_cast<BaseExpr>(expr); } // 需要修改返回新节点副本 return Int::Make(expr->value_ + 1); }; virtual ExprPtr Visit(std::shared_ptr<const String> expr) { // 无需修改直接返回原节点 bool need_modify = true; if (!need_modify) { return std::const_pointer_cast<BaseExpr>(expr); } // 需要修改返回新节点副本 return String::Make(expr->value_ + "_modified"); }; }; template<typename T> ExprPtr Expr<T>::Mutate(ExpressionTreeMutator* visitor) const { // 从this获取对应shared_ptr,转型为派生类类型后传给访问者 auto self = std::static_pointer_cast<const T>(shared_from_this()); return visitor->Visit(self); }
注意事项
- 所有
BaseExpr派生类的实例必须由std::shared_ptr持有,禁止栈创建、禁止用裸指针管理生命周期,否则调用shared_from_this()会抛出std::bad_weak_ptr异常 - 示例中给每个节点新增了静态
Make方法,强制所有实例都通过shared_ptr创建,避免不符合使用前提的错误用法 - 返回原节点时使用的
std::const_pointer_cast是安全的,因为你的设计要求原始节点禁止直接修改,访问者不会修改传入的const节点内容
内容的提问来源于stack exchange,提问作者senloa
相关产品推荐
相关产品推荐

