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

含std::unique_ptr的类B无法嵌套指向?编译错误排查与解决

问题:含std::unique_ptr的对象无法嵌套持有同类实例?

我编写了如下最小可复现示例,发现包含std::unique_ptr的对象B似乎无法持有指向另一个B实例的std::unique_ptr:

#include <iostream>
#include <memory>

struct C {
  virtual void say_name() = 0;
};

struct A : public C {
  int value;
  A(int value) : value(value) {}
  void say_name() override { std::cout << "A" << value << std::endl; }
};

struct B : public C {
  std::unique_ptr<C> ptr;
  B(std::unique_ptr<C> ptr) : ptr(std::move(ptr)) {}
  void say_name() override { std::cout << "B" << std::endl; }
};

int main() {
  A a(5);
  B b(std::make_unique<A>(10));
  // 这里创建另一个B会报错
  B b2(std::make_unique<B>(b));
}

预期b持有A,b2持有b,但创建b2时出现错误,提示调用了被隐式删除的B的拷贝构造函数。完整错误信息如下:

In template: call to implicitly-deleted copy constructor of 'B'clang(ovl_deleted_special_init)
unique_ptr.h(767, 30): Error occurred here
main.cpp(33, 13): In instantiation of function template specialization 'std::make_unique<B, B &, 0>' requested here
main.cpp(24, 22): Copy constructor of 'B' is implicitly deleted because field 'ptr' has a deleted copy constructor
unique_ptr.h(221, 55): Copy constructor is implicitly deleted because 'unique_ptr<C>' has a user-declared move constructor

错误原因

  • std::unique_ptr是独占所有权的智能指针,它的拷贝构造函数被显式删除——毕竟独占所有权的指针不能被复制,否则会出现多个指针管理同一块内存的矛盾。
  • B类包含std::unique_ptr<C>成员,当类成员包含不可拷贝的类型时,编译器会自动删除该类的拷贝构造函数。
  • 创建b2时,std::make_unique<B>(b)尝试将b作为参数传递,但B的构造函数需要std::unique_ptr<C>,此时编译器会尝试拷贝b来构造内部的B实例,而B的拷贝构造函数已被删除,因此报错。

修复方案

根据你的需求选择对应的方案:

1. 转移所有权(让b2独占原b的资源)

如果希望b2完全接手b持有的资源,之后b不再使用,可以用std::move转移所有权:

int main() {
  A a(5);
  B b(std::make_unique<A>(10));
  // 用std::move转移b的所有权给新创建的B实例
  B b2(std::make_unique<B>(std::move(b)));
  // 注意:此时b的ptr已被转移,不能再使用b
}

std::move(b)会触发B的移动构造函数(编译器会自动生成,因为unique_ptr支持移动),将b的资源转移给新的B对象,再被b2的ptr持有。

2. 共享所有权(让b2和b共享同一资源)

如果需要多个B实例共享同一内部对象的所有权,将std::unique_ptr替换为std::shared_ptr(共享所有权的智能指针,支持拷贝):

struct B : public C {
  std::shared_ptr<C> ptr;
  B(std::shared_ptr<C> ptr) : ptr(std::move(ptr)) {}
  void say_name() override { std::cout << "B" << std::endl; }
};

int main() {
  A a(5);
  auto b_shared = std::make_shared<B>(std::make_shared<A>(10));
  B b2(b_shared);
  // 此时b_shared和b2.ptr都指向同一个B对象,该对象持有A实例
}

std::shared_ptr通过引用计数管理内存,当最后一个指向对象的shared_ptr销毁时,内存才会被释放。

3. 持有对象引用(不转移/共享所有权)

如果只是想让b2持有b的引用,且能保证b的生命周期长于b2,可以改用裸指针(但裸指针需手动管理内存,风险较高):

struct B : public C {
  C* ptr;
  B(C* ptr) : ptr(ptr) {}
  void say_name() override { std::cout << "B" << std::endl; }
  // 注意:此处不负责释放ptr的内存,所有权在外部
};

int main() {
  A a(5);
  // 手动管理A的内存,释放unique_ptr的所有权
  B b(std::make_unique<A>(10).release());
  B b2(&b);
  
  b.say_name();
  b2.say_name();
  
  // 手动释放A的内存
  delete b.ptr;
}

这种方式需严格控制对象生命周期,避免出现悬空指针,非必要不推荐。


内容的提问来源于stack exchange,提问作者Featherball

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 18:34:52