当前位置:网站首页>本周小贴士#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++
边栏推荐
- rpcms获取指定分类下的文章的方法
- 【TPM2.0原理及应用指南】 16、17、18章
- LeetCode 1981. Minimize the difference between the target value and the selected element one question per day
- Solid function learning
- 99% of users often make mistakes in power Bi cloud reports
- SlashData开发者工具榜首等你而定!!!
- How to implement safety practice in software development stage
- 99% 用户在 Power BI 云端报表常犯错误
- 赋能智慧电力建设 | 麒麟信安高可用集群管理系统,保障用户关键业务连续性
- 【Seaborn】组合图表、多子图的实现
猜你喜欢
麒麟信安携异构融合云金融信创解决方案亮相第十五届湖南地区金融科技交流会
责任链模式 - Unity
Skimage learning (1)
Skimage learning (3) -- adapt the gray filter to RGB images, separate colors by immunohistochemical staining, and filter the maximum value of the region
【网络攻防原理与技术】第5章:拒绝服务攻击
Skimage learning (3) -- gamma and log contrast adjustment, histogram equalization, coloring gray images
With the latest Alibaba P7 technology system, mom doesn't have to worry about me looking for a job anymore
Several best practices for managing VDI
Reflections on "product managers must read: five classic innovative thinking models"
Nerf: the ultimate replacement for deepfake?
随机推荐
Matplotlib绘制三维图形
如何在软件研发阶段落地安全实践
麒麟信安中标国网新一代调度项目!
阿富汗临时政府安全部队对极端组织“伊斯兰国”一处藏匿点展开军事行动
How to mount the original data disk without damage after the reinstallation of proxmox ve?
99% of users often make mistakes in power Bi cloud reports
Flash build API service
如何在软件研发阶段落地安全实践
Proxmox VE重装后,如何无损挂载原有的数据盘?
A tour of grpc:03 - proto serialization / deserialization
网络攻防复习篇
The mail server is listed in the blacklist. How to unblock it quickly?
LeetCode 1155. N ways to roll dice one question per day
【网络攻防原理与技术】第6章:特洛伊木马
SlashData开发者工具榜首等你而定!!!
【饭谈】如何设计好一款测试平台?
The server is completely broken and cannot be repaired. How to use backup to restore it into a virtual machine without damage?
Mrs offline data analysis: process OBS data through Flink job
麒麟信安云平台全新升级!
Sator a lancé le jeu web 3 "satorspace" et a lancé huobi