当前位置:网站首页>本周小贴士131:特殊成员函数和`= default`
本周小贴士131:特殊成员函数和`= default`
2022-07-07 15:39:00 【-飞鹤-】
作为totw#131最初发表于2017年3月24日
由James Dennett ([email protected])创作
从一开始,C++ 就支持一些所谓的特殊成员函数的编译器声明版本:默认构造函数、析构函数、复制构造函数和复制赋值运算符。 C++11 向列表添加了移动构造和移动赋值,并添加了语法(=default 和 =delete)以控制何时声明和定义这些默认值。
=default 有什么作用,我们为什么要使用它?
写 =default 是我们告诉编译器“你通常会为这个特殊成员函数做的事情”的方式。 为什么我们要这样做而不是手动编写实现或让编译器为我们声明一个?
- 我们可以更改访问级别(例如,使构造函数受保护而不是公共),使析构函数为虚拟,或恢复将被抑制的函数(例如,具有其他用户声明的构造函数的类的默认构造函数)并且仍然 让编译器为我们生成函数。
- 如果复制/移动成员就足够了,编译器定义的复制和移动操作不需要在每次添加或删除成员时进行维护。
- 编译器提供的特殊成员函数可以是不重要的(当它们调用的所有对自身的操作是不重要的),这可以使它们更快、更安全。
- 具有默认构造函数的类型可以是聚合的,因此支持聚合初始化,而具有用户提供的构造函数的类型则不能。
- 显式声明一个默认成员为我们提供了一个地方,用来记录结果函数语义。
- 在类模板中,=default 是一种简单方法,去有条件地声明操作,具体取决于某些基础类型是否提供它。
当我们在特殊成员函数的初始声明中使用 =default 时,编译器将检查它是否可以为该函数合成为内联定义。 如果可以,它就进行。 如果不能,该函数实际上被声明为已删除,就像我们写了 =delete 一样。 这正是我们透明地包装类所需要的(例如,如果我们正在定义一个类模板),但读者可能会感到惊讶。
如果函数的初始声明使用 =default,或者如果编译器声明了一个非用户声明的特殊成员函数,则会推导出适当的 noexcept 规范,从而可能允许更快的代码。
它是如何工作的呢?
在C++11之前,如果我们需要一个默认的构造函数或已经有了其他的构造函数,那么我们可以这样写:
class A {
public:
A() {
} // User-provided, non-trivial constructor makes A a non-aggregate.
};
从C++11开始我们有更多选择:
class C {
public:
C() = default; // misleading: C has a deleted default constructor
private:
const int i; // const => must always be initialized.
};
class D {
public:
D() = default; // unsurprising, but not explicit: D has a default constructor
private:
std::unique_ptr<int> p; // std::unique_ptr has a default constructor
};
显然,我们不应该编写类 C 之类的代码:在非模板中,仅当您希望该类支持该操作(然后测试它是否支持)时才使用 =default。 clang-tidy 包括对此的检查。
当在特殊成员函数的第一次声明之后(即在类之外)使用 =default 时,它具有更简单的含义:它告诉编译器定义函数,并在无法这样做时给出错误.当在类外使用 =default 时,默认函数将不是微不足道的:微不足道由第一个声明确定(因此所有客户端都同意操作是否微不足道)。
如果你不需要你的类是一个聚合并且你不需要构造函数是微不足道的,那么在类定义之外默认构造函数,比如下面的示例 E 和 F,通常是一个不错的选择。它的含义对读者来说是清楚的,并由编译器检查。对于默认构造函数或析构函数的特殊情况,我们可以写 {} 而不是 =default,但对于其他默认操作,编译器生成的实现不那么简单,为了保持一致性,最好在所有适用情况下写 =default。
class E {
public:
E(); // promises to have a default constructor, but...
private:
const int i; // const => must always be initialized.
};
inline E::E() = default; // compilation error here: would not initialize `i`
class F {
public:
F(); // promises to have a default constructor
private:
std::unique_ptr<int> p; // std::unique_ptr has a default constructor
};
inline F::F() = default; // works as expected
建议
优先 =default 而不是手动编写等效的实现,即使该实现只是 {}。 可选地,从初始声明中省略 =default 并提供单独的默认实现。
注意默认的移动操作。 来自移动的对象仍然必须满足其类型的不变量,并且默认实现通常不会保留字段之间的关系。
在模板之外,如果 =default 不提供实现,则改为使用 =delete。
边栏推荐
猜你喜欢

【视频/音频数据处理】上海道宁为您带来Elecard下载、试用、教程

The top of slashdata developer tool is up to you!!!

With the latest Alibaba P7 technology system, mom doesn't have to worry about me looking for a job anymore

99%的人都不知道|私有化部署还永久免费的即时通讯软件!

科普达人丨一文弄懂什么是云计算?

Sator推出Web3游戏“Satorspace” ,并上线Huobi

Lex & yacc of Pisa proxy SQL parsing

测试用例管理工具推荐

Pychart ide Download

麒麟信安中标国网新一代调度项目!
随机推荐
LeetCode 1696. Jumping game VI daily question
自定义View必备知识,Android研发岗必问30+道高级面试题
Pychart ide Download
Solidity函数学习
Problems encountered in Jenkins' release of H5 developed by uniapp
Seaborn数据可视化
Notes on installing MySQL in centos7
【网络攻防原理与技术】第5章:拒绝服务攻击
[fan Tan] those stories that seem to be thinking of the company but are actually very selfish (I: building wheels)
Leetcode brush questions day49
Skimage learning (3) -- adapt the gray filter to RGB images, separate colors by immunohistochemical staining, and filter the maximum value of the region
Skimage learning (3) -- gamma and log contrast adjustment, histogram equalization, coloring gray images
LeetCode 648(C#)
Biped robot controlled by Arduino
如何在软件研发阶段落地安全实践
L1-028 判断素数(Lua)
PLC:自动纠正数据集噪声,来洗洗数据集吧 | ICLR 2021 Spotlight
无法链接远程redis服务器(解决办法百分百)
Nerf: the ultimate replacement for deepfake?
QT picture background color pixel processing method