现代C++之字面量、静态断言和成员函数说明符
学习自:极客时间现代C++!
1.自定义字面量
字面量(literal)是指在源代码中写出的固定常量,它们在 C++98 里只能是原生类型,如:
"hello",字符串字面量,类型是 const char[6]
3.14f,浮点数字面量,类型是 float
C++11 引入了自定义字面量,可以使用 operator"" 后缀 来将用户提供的字面量转换成实际的类型。C++14 则在标准库中加入了不少标准字面量。下面这个程序展示了它们的用法:
#include <chrono>
#include <complex>
#include <iostream>
#include <string>
#include <thread>
#include <bitset>
using namespace std::literals::chrono_literals;
using namespace std::literals::string_literals;
using namespace std::literals::complex_literals;
int main() {
std::cout << "i * i = " << 1i * 1i << std::endl;
std::cout << "Waiting for 500ms" << std::endl;
std::this_thread::sleep_for(500ms);
std::cout << "Hello world"s.substr(0, 5) << std::endl;
length l1 = length(1.0, length::metre);
length l2 = length(1.0, length::centimetre);
std::cout << l2.value << std::endl;
std::cout << (l1 + l2).value << std::endl;
}
现在我们有个类:
struct length {
double value;
enum unit {
metre,
kilometre,
millimetre,
centimetre,
inch,
foot,
yard,
mile,
};
static constexpr double factors[] =
{1.0, 1000.0, 1e-3,
1e-2, 0.0254, 0.3048,
0.9144, 1609.344};
explicit length(double v,
unit u = metre) {
value = v * factors[u];
}
};
length operator+(length lhs,
length rhs) {
return length(lhs.value +
rhs.value);
}
我们想完成两个length
的加法,我们可以这样写:
length l1 = length(1.0, length::metre);
length l2 = length(1.0, length::centimetre);
std::cout << l2.value << std::endl;
std::cout << (l1 + l2).value << std::endl;
有了自定义字面量后,我们可以重载""
:
length operator "" _m(long double v) { return length(v, length::metre); }
length operator "" _cm(long double v) { return length(v, length::centimetre); }
此时可以这样写:
// 1.0_m + 10.0_cm
std::cout << (1.0_m + 1.0_cm).value << std::endl;
2.二进制字面量
直接前缀加0b
,跟8、16等进制用法一样!
// 二进制字面量
unsigned mask = 0b1101;
// 以十进制打印
std::cout << mask << std::endl;
// 打印二进制字面量
std::cout << std::bitset<4>(mask) << std::endl;
就是打印的时候得用bitset
才行。
3.数字分隔符
按照自己想断的地方敲上'
即可!
// 数字分隔符
unsigned mk = 0b111'000'000;
double pi = 3.141'5926;
4.静态断言
C++98 的 assert 允许在运行时检查一个函数的前置条件是否成立。没有一种方法允许开发人员在编译的时候检查假设是否成立。C++11引入了static_assert这个关键字,用来做编译期间的断言,因此叫做静态断言。
例如下面例子:
在编译期就可以判断条件是否成立(alignment是否是2的平方)。
const int alignment=5;
static_assert((alignment & (alignment - 1)) == 0, "Alignment must be power of two");
而assert
则做不到:
assert((alignment & (alignment - 1)) == 0);
5.default 和 delete 成员函数
在类的定义时,C++ 有一些规则决定是否生成默认的特殊成员函数。这些特殊成员函数可能包括:
默认构造函数
析构函数
拷贝构造函数
拷贝赋值函数
移动构造函数
移动赋值函数
我们可以改变缺省行为,在编译器能默认提供特殊成员函数时将其删除,或在编译器不默认提供特殊成员函数时明确声明其需要默认提供(不过,要注意,即使用户要求默认提供,编译器也可能根据其他规则将特殊成员函数标为删除)。
例如:
class myFun {
public:
myFun() = default;
myFun(const myFun &) = default;
myFun &operator=(const myFun &) = default;
myFun(myFun &&) = delete;
myFun &operator=(myFun &&) = delete;
~myFun() = default;
};
6.override 和 final 说明符
override 和 final 是两个 C++11 引入的新说明符。它们不是关键词,仅在出现在函数声明尾部时起作用,不影响我们使用这两个词作变量名等其他用途。这两个说明符可以单个或组合使用,都是加在类成员函数声明的尾部。
(1)override
功能:
显式声明了成员函数是一个虚函数且覆盖了基类中的该函数。如果有 override 声明的函数不是虚函数,或基类中不存在这个虚函数,编译器会报告错误。
作用:
给开发人员更明确的提示,这个函数覆写了基类的成员函数;
让编译器进行额外的检查,防止程序员由于拼写错误或代码改动没有让基类和派生类中的成员函数名称完全一致。
(2)final
功能:
声明了成员函数是一个虚函数,且该虚函数不可在派生类中被覆盖。
位置:成员函数后面
标志某个类或结构不可被派生。
位置:类/结构体后面
例如:
class A {
public:
virtual void foo();
virtual void bar();
void foobar();
};
class B : public A {
public:
void foo() override; // OK
void bar() override final; // OK
//void foobar() override;
// 非虚函数不能 override
};
class C final : public B {
public:
void foo() override; // OK
//void bar() override;
// final 函数不可 override
};
class D : public C {
// 错误:final 类不可派生
};