当前位置:网站首页>本周小贴士#134:make_unique与私有构造函数
本周小贴士#134:make_unique与私有构造函数
2022-07-07 15:39:00 【-飞鹤-】
作为totw#134最初发表于2017年5月10日
由谷歌工程师Yitzhak Mandelbaum创作
因此,你阅读了小贴士#126并准备留下一些新的东西。一切都是正常的,直到你尝试使用absl::make_unique并使用私有构造函数去构造对象,但是编译失败。让我们看一下这个问题的一个具体示例,以便理解哪里出了问题。然后,我们可以讨论一些解决方案。
示例:制造小部件
你正在定义一个类来展示小部件。每个小部件都有一个标识符,这些标识符受某些约束。为了确保一直满足这些约束,你将Widget类的构造函数声明为私有,并为用户提供工厂函数Make,用于生成具有合适标识符的小部件。(有关为什么工厂函数优于初始化方法的建议,请参阅技巧 #42。)
class Widget {
public:
static std::unique_ptr<Widget> Make() {
return absl::make_unique<Widget>(GenerateId());
}
private:
Widget(int id) : id_(id) {
}
static int GenerateId();
int id_;
}
当你尝试编译时,你将得到如下的错误:
error: calling a private constructor of class 'Widget'
{
return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
^
note: in instantiation of function template specialization
'absl::make_unique<Widget, int>' requested here
return absl::make_unique<Widget>(GenerateId());
^
note: declared private here
Widget(int id) : id_(id) {
}
当make已经访问私有构造函数时,absl::make_unique无法工作!注意,这个问题也出现在友元下。例如:Widget的友元使用absl::make_unique去构造Widget会有同样的问题。
建议
我们推荐以下的任一个替代方案:
- 使用new和absl::WrapUnique,但请解释你的选择。例如:Widget的友元使用absl
// 使用new访问非仅有构造函数 return absl::WrapUnique(new Widget(...));
- 考虑构造函数是否可以安全地公开。如果是这样,那么在适合直接构造时将其公开并记录。
在很多情况下,将构造函数标记为私有是过度设计。在这些情况下,最好的解决方案是将你的构造函数标记为公有并记录它们的正确使用。然而,如果你的构造函数需要设置为私有(比如说,为了确保类不变量),则使用new和WrapUnique。
为什么我不能友元absl::make_unique?
你可能想友元absl::make_unique,这样它就可以访问你的私有构造函数。这是一个糟糕的主意,有几个原因:
首先,虽然对友元实践的全面讨论超过出本贴士的范围,但是一个好的经验法则是“不要远距离的朋友关系”。否则,你将创建一个友元竞争声明,一个不由所有者维护的声明。另外请参见风格指南建议。
其次,请注意,你依赖于absl::make_unique的实现细节,即它直接调用new。如果它被重构以间接调用new——例如,在构造C++14或更高版本中,absl::make_unique是std::make_unique的别名,并且友元声明是无效的。
最后,通过友元absl::make_unique,你允许任何人以这种方式创建对象,那么为什么不将你的构造函数声明为公有,然后完全避免这个问题呢?
std::shared_ptr呢?
对于std::shared_ptr,情况有些不同。此处没有absl::WrapSahred和模拟(std::shared_ptr(new T(…)))涉及两个分配,其中std::make_shared可以用一个来完成。如果这种差异很重要,那么请考虑习惯用法:让构造函数采用只有特定代码才能创建的特殊标记。例如:
class Widget {
class Token {
private:
Token() {
}
friend Widget;
};
public:
static std::shared_ptr<Widget> Make() {
return std::make_shared<Widget>(Token{
}, GenerateId());
}
Widget(Token, int id) : id_(id) {
}
private:
static int GenerateId();
int id_;
};
关于习惯用法,参考以下文章:
More Useful Empty Classes
Passkey Idiom and Better Friendship in C++
边栏推荐
- Proxmox VE重装后,如何无损挂载原有的数据盘?
- [Fantan] how to design a test platform?
- Number of exchanges in the 9th Blue Bridge Cup finals
- The server is completely broken and cannot be repaired. How to use backup to restore it into a virtual machine without damage?
- 【饭谈】那些看似为公司着想,实际却很自私的故事 (一:造轮子)
- 《世界粮食安全和营养状况》报告发布:2021年全球饥饿人口增至8.28亿
- 阿富汗临时政府安全部队对极端组织“伊斯兰国”一处藏匿点展开军事行动
- Skimage learning (2) -- RGB to grayscale, RGB to HSV, histogram matching
- 自定义View必备知识,Android研发岗必问30+道高级面试题
- 【信息安全法律法規】複習篇
猜你喜欢
Sator推出Web3遊戲“Satorspace” ,並上線Huobi
Leetcode brush questions day49
责任链模式 - Unity
Skimage learning (3) -- adapt the gray filter to RGB images, separate colors by immunohistochemical staining, and filter the maximum value of the region
测试用例管理工具推荐
Sator a lancé le jeu web 3 "satorspace" et a lancé huobi
如何在博客中添加Aplayer音乐播放器
Sator launched Web3 game "satorspace" and launched hoobi
How to add aplayer music player in blog
99%的人都不知道|私有化部署还永久免费的即时通讯软件!
随机推荐
【网络攻防原理与技术】第7章:口令攻击技术 第8章:网络监听技术
从DevOps到MLOps:IT工具怎样向AI工具进化?
A tour of grpc:03 - proto serialization / deserialization
无法链接远程redis服务器(解决办法百分百)
QML beginner
【饭谈】Web3.0到来后,测试人员该何去何从?(十条预言和建议)
SIGGRAPH 2022最佳技术论文奖重磅出炉!北大陈宝权团队获荣誉提名
Solid function learning
How to add aplayer music player in blog
DNS 系列(一):为什么更新了 DNS 记录不生效?
mysql实现两个字段合并成一个字段查询
Seaborn data visualization
【TPM2.0原理及应用指南】 9、10、11章
浅谈 Apache Doris FE 处理查询 SQL 源码解析
Mrs offline data analysis: process OBS data through Flink job
服务器彻底坏了,无法修复,如何利用备份无损恢复成虚拟机?
LeetCode 890(C#)
如何在博客中添加Aplayer音乐播放器
How to implement safety practice in software development stage
Matplotlib绘制三维图形