C++11中私有构造类静态方法返回shared_ptr实例编译报错问题
问题背景
我希望禁止用户通过构造函数直接创建类实例,因此将构造函数标记为私有方法。同时需要提供一个方法,返回可以自动管理实例生命周期的对象,最初设计的该方法为getFoo()。
初版代码
#include <iostream> #include <memory> class Foo : public std::enable_shared_from_this<Foo> { private: // 禁止用户直接通过构造函数创建实例 Foo(int num):num_(num) { std::cout << "Foo::Foo\n"; } public: ~Foo() { std::cout << "Foo::~Foo\n"; } std::shared_ptr<Foo> getFoo() { return shared_from_this(9); } private: int num_; };
由于构造函数设为私有后,没有任何途径可以创建类实例,getFoo方法永远无法被调用,因此对代码做了修改。
修改后无法编译的代码
#include <iostream> #include <memory> class Foo : public std::enable_shared_from_this<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() { return std::make_shared<Foo>(5); } private: int num_; }; int main() { auto pf = Foo::Create(); }
编译报错核心信息:
error: 'Foo::Foo(int)' is private within this context 119 | ::new((void*)__p) _Tp(std::forward<_Args>(__args)...); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <source>:6:5: note: declared private here 6 | Foo(int num):num_(num) { std::cout << "Foo::Foo\n"; } | ^~~
原本认为类的所有成员函数都有权限访问类的私有成员,不清楚错误原因,且开发环境仅支持C++11标准。初步猜测是因为Create()是静态函数,无权调用非静态的构造函数,但不确定该判断是否正确。
问题解答
你的猜测不成立。静态成员函数拥有和普通成员函数完全一致的类私有成员访问权限,编译报错的核心原因是:实际调用Foo私有构造函数的代码不是Create()本身,是标准库中std::make_shared的内部实现——这段代码不属于Foo类的成员范畴,自然没有访问Foo私有成员的权限。
C++的访问控制校验只看「执行访问操作的代码所在的作用域」,不会因为外层调用方是类成员,就给嵌套调用的、不属于类的外部代码开放私有访问权限。
C++11 兼容的解决方案
不使用std::make_shared,直接在类成员函数内部手动构造实例再交给shared_ptr管理即可,此时构造Foo实例的代码位于Create()作用域内,可以正常访问私有构造函数:
#include <iostream> #include <memory> class Foo : public std::enable_shared_from_this<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() { return std::shared_ptr<Foo>(new Foo(5)); } private: int num_; }; int main() { auto pf = Foo::Create(); }
方案说明
- 该实现完全符合C++11标准,不需要更高版本语法支持
- 相比
std::make_shared会存在一次额外的控制块内存分配开销,但可以完美解决私有构造函数的访问权限问题 - 禁止用户直接构造实例的需求完全满足:外部代码无法调用私有构造函数,只能通过
Create()方法获取托管的实例指针
补充:如果后续可以升级到C17及以上版本,还可以通过自定义分配器实现和
make_shared一致的单次内存分配效果,但C11环境下上述写法是兼容性、可读性最均衡的实现。
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

