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

C++私有构造类返回shared_ptr时new可编译make_shared报错原因

C++私有构造函数场景下new与make_shared编译行为差异解析

问题现象

存在构造函数为私有属性的类Foo,在其静态工厂方法中两种构造shared_ptr的写法编译表现不一致:

  • 写法1:先用new Foo(5)在堆上构造实例,再将返回的裸指针传入std::shared_ptr构造函数包装返回,可正常编译通过
  • 写法2:直接调用std::make_shared<Foo>(5)构造并返回shared_ptr,会触发编译错误,提示无法访问私有构造函数

可复现问题的代码片段如下:

#include <iostream>
#include <memory>
 
class Foo {
private:
    // 禁止外部直接调用构造函数创建实例
    Foo(int num):num_(num) { std::cout << "Foo::Foo\n"; }
public:
    ~Foo() { std::cout << "Foo::~Foo\n"; } 
    static std::shared_ptr<Foo> Create() {
#if WORKS_WELL
        // 该写法可正常编译
        Foo *foo = new Foo(5);
        return std::shared_ptr<Foo>(foo);
#else
        // 该写法编译失败
        return std::make_shared<Foo>(5);
#endif
    }
private:
    int num_;
};
 
int main() {
    auto pf = Foo::Create(); 
}

根本原因

两种写法的核心差异在于调用私有构造函数的上下文不同,C++的访问控制规则只检查调用发生时所处的上下文是否具备对应成员的访问权限:

  • 第一种写法中,new Foo(5)的调用位置完全处于Foo类的静态成员函数Create()内部。根据C++规则,类的成员函数可以访问自身类的所有私有成员,包括私有构造函数,因此这一步构造是合法的。后续用已经构造完成的Foo*裸指针构造std::shared_ptr时,shared_ptr的构造函数只负责接管指针所有权,不会再次调用Foo的构造函数,全程不会触发权限问题。
  • 第二种写法中,std::make_shared是标准库实现的独立全局模板函数,既不属于Foo类的成员,也没有被Foo声明为友元。它的内部逻辑是一次性分配容纳Foo对象和引用计数控制块的连续内存,再在自身函数上下文中直接调用Foo的构造函数完成对象初始化。这个构造调用发生在std::make_shared的函数上下文里,不属于Foo类的成员访问范畴,自然没有权限调用Foo的私有构造函数,最终导致编译失败。

常见适配方案

  • 如果对make_shared的一体式内存分配(减少内存分配次数、提升缓存局部性)没有强需求,可以继续使用new构造后包装为shared_ptr的写法,做好异常安全校验即可
  • 如果需要使用make_shared优化性能,可以将对应std::make_shared的实现声明为Foo类的友元,或者通过*钥匙访问模式(passkey idiom)*在不公开构造函数的前提下给make_shared开放构造权限

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 12:01:12