You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++实现n叉树变更访问者模式时shared_ptr相关问题咨询

解决方案

你遇到的核心问题是如何从已被std::shared_ptr管理的对象的this裸指针,获取对应引用计数正确的智能指针,C++标准库提供的std::enable_shared_from_this是专门处理这类场景的原生方案,无需额外封装持有指针的类。

核心修改步骤

  1. 让BaseExpr继承std::enable_shared_from_this<BaseExpr>,获得从this创建shared_ptr的能力
  2. 调整Visit方法的入参为std::shared_ptr<const T>,避免裸指针构建智能指针的非法操作
  3. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 07:57:03