第一章 速成和标准库
1. 名词解释
union
非受限联合体volatile
类型修饰符,修饰被不同类型访问和修改的变量vector
向量,封装了动态大小数组optional
2. 预处理器指令(Preprocessor Directives)
-
声明通常在头文件中,通常使用扩展名为
.h
,在c++中省略后缀.h
,而加入了前缀c
-
定义通常在源文件,通常使用扩展名为
.cpp
预处理器指令 功能 #include<file>
指定的文件插入代码中指定的位置 #define [id] [value]
指定标识符的每次出现会被替换为指定的值 #ifdef [id]
#endif
#ifndef[id]
#endif
防止被重复引用 #pragma [xyz]
预处理过程中到达指令时显示警告或错误 #ifndef MYHEADER_H #define MYHEADER_H #endif //如果你的代码支持#pragma once #pragma once
3. 主函数
-
主函数要么不引用参数,要么就引用以下两个参数
int main(int argc,char* argv[]) //argc给出了传递给程序的参数的数量,而argv包含这些参数。
-
注意
argv[0]
可以是程序名字,也可以是空字符串
4. I/O Streams
-
cout
:输出到用户控制台
ceer
:输出到错误控制台 -
c++20组合输出字符串和数字,可以用
std::format()
,被<format>
定义,以执行字符串格式化std::cout<<std::format("There are {} ways I love you.",219) <<std::endl;
换码顺序 解释 \n 换行 \r 回车-回到当前行的开头,继续输出内容会被覆盖 \t Tab \\
反斜线字符 \
\"
引号 "
在一条流中,使用一次 endl 即可,插入一条新行并刷新当前缓冲区,\n只能实现换行
5. 命名空间
-
当两个函数的名称相同时,为了区分,可以使用命名空间 Namespace
//第三方库函数foo()和自写的foo()函数 namespace mycode{ void foo(){ std::cout<<format("foo() called in the mycode namespace {}",129)<<std::endl; } } //scope resolution operator 作用域解析操作符 mycode::foo(); ```
-
属于mycode命名空间内的任何代码都可以调用同意名称空间内的其他代码,无需前置该名称空间,例如:
```c++ using namespace mycode; int main(){ foo(); } ```
-
像 cout 和 endl 就是在 std 名称空间中定义的名称,所以:
#include<iostream> using namespace std; int main(){ cout<<"hello,world"<<endl; }
6. Nested Namespace 嵌套命名空间
-
用双冒号分隔
//c++17及以上版本 namespace MyLinraries::Networking::FTP{}
//旧版本 namespace MyLibraries{ namespace Nteworking{ namespace FTP{ } } }
7. Namespace Alias 命名空间别名
namespace MyFTP= MyLibraries::Nteworking::FTP;
8. Literals
-
一些标准的literals用来表示数字或字符串
Decimal literal, 123
Octal literal, 0173
Hexadecimal liter, 0x7B
Binary literal, 0b1111011 -
数字分隔符 ’ 可以在数字文字中使用
23’456’789
0.123’456f
9. Variables 变量
-
变量可以在不给定值的情况下声明。这些未初始化的变量通常会以一个基于当时内存中的任何内容的半随机值结束,是无数bug的来源。下边是两种类型的变量声明:
int uninitializedInt; //random value int initializedInt {7}; //assigned an initial value
//赋值语法 int initializrInt = 7;
-
c++的变量是强类型的,以下是一些最常见的内置类型:
TYPE DESCRIPTION USAGE (signed) int
signed正整数或负整数(4 bytes) int i {-7};
signed int i {-6};
signed i {-5};(signed) short (int) 短整数(2 bytes) short s {13};
short int s {14};
signed short s {15};
signed short int s {16};(signed) long (int) 长整数(4 bytes) long l {-7L} (signed) long long (int) long long整数 long long ll {14LL}; unsigned (int)
unsigned short (int)
unsigned long (int)
unsigned long long (int)将上述值限定在 >= 0 unsigned int i {2U};
unsigned j {5U};
unsigned short s {23U};
unsigned long l {5400UL};
unsigned long long ll {140ULL};float 浮点数 float f {7.2f}; double 双精度的数字;精度至少与 float 相同 double d {7.2}; long double 长双精度数;精度至少与 double 相同 long double d {16.98L}; char
…字符 char ch {‘m’}; char8_t (since C++20)
char16_t
char32_tn-bit UTF-n-encoded Unicode,n可以是8,16,32 char8_t c8 {u8’m’};
char16_t c16 {u’m’};
char32_t c32 {U’m’};wchar_t 单个宽字符 wchar_t w {L’m}; bool 布尔类型,可以为true或false之一 bool b {true}; 和字符相关的是,
<cstddef>
提供了表示单个字节的 std::byte 类型。c++17之前,char 和 unsigned char 用来表示一个字节,但看起来像在处理字符。字节初始化如下:std::byte b {42};
10. Numerical Limits 数值限制
-
std::numeric_limits
来自<limits>
,由于它是一个类模板,所以必须在角括号里指定类型。例如:cout<<"int:\n"; cout<<format("max int value:{}\n",numeric_limits<int>::max()); cout<<format("min int value:{}\n",numeric_limits<int>::min()); //lowest替换 min //将上述代码中 int替换为 double
//输出 int: Max int value:2147483647 min int value:-2147483648 lowest int value:-2147483648 double: Max double value: 1.79769e+308 Min double value: 2.22507e-308 Lowest double value: -1.79769e+308
注意 min()和 lowest()是有区别的。对于整数来说,数值是一样的,但对于浮点型,min可表示最小正值,而 lowest是最负值,等于 -max()。
11. 零初始化
- 用 {0} 或者 {} 统一初始化器
char、int 等初始化为 0
浮点型初始化为 0.0
指针类型初始化为 nullptr
12. Casting 强制转换
-
三种方法,推荐第三种,复杂但最干净
float myFloat {3.14f}; int i1 {(int)myFloat}; int i2 {int(myFloat)}; int i3 {static_cast<int>(myFloat)};
//short 可以自动转换为 long,因为 long 表示相同的数据类型(精度) long someLong {someShort};
自动强转时,注意数据的潜在丢失。例如:浮点值表示的数字是一个比最大可表示整数值大的数字,得到的整数可能是完全错误的。
如果确定左右类型完全兼容,可以进行隐式强制转换。
13. 浮点数
- 具有不同数量级的浮点值的计算会导致误差
- 计算两个几乎相同的浮点数之间的差值会导致精度的损失
- 很多十进制值不能准确的表示为浮点数
- 特殊的浮点数:
+/- infinity 正负无穷
NaN 非数 std::isnan()
检查浮点数是否为数字
std::isinf()
检查无穷大
numeric_limits<double>::infinity
获得浮点数
14. Operators 操作符
OPERATOR | DESCRIPTION | USAGE |
---|---|---|
& &= | 有0则0 AND | i = j & k(取小) j &= k |
| |= | 有1则1 OR | i = j | k(取大) j |= k |
<< >> <<= >>= | 将每个bit向左或向右”移动“指定数量的位置,高位舍弃,地位补零 | i = i << 1; i = i >> 4; i <<= 1; i >>= 4; |
<br>= | 异或XOR 11得0 | i = i ^ j; i ^= j; |
~ | 按位求补 | |
- 优先级排序
++ – 后缀
! ++ – 前缀
*
/ %
+
-
<< >>
&
^
|
= += -= *= /= %= &= |= ^= <<= >>=
15. Enumerated Types 枚举类型
-
枚举类型允许定义自己的序列,以便使用该序列中的值声明变量。例如:
//国际象棋 const int pieceTypeKing {0}; const int pieceTypeQueen {1}; const int pieceTypeRook {3}; const int pieceTypePawn {4}; int myPiece {PieceTypeking};
这样操作其实很危险,并没有限制范围,-1操作没有对应的常熟
-
strongly typed enumeration types 通过严格定义变量的值范围来解决这个问题
//自己指定枚举类型的成员的值 enum class PieceType { king=1, Queen, Rook=10, Pawn }; //如果不为枚举成员赋值,编译器会自动为其分配一个递增为1的先前枚举成员的值。 //如果第一个不赋值,编译器赋值为0 PieceType piece {PieceType::king};
-
枚举值在内部用整数值表示,也不会自动转为整数,以下内容是非法的
if(PieceType::Queen == 12){...}
-
默认,枚举值的基础类型是整数,但是可以如下整改:
enum class PieceType : unsigned long{ King = 1; Queen, Rook = 10; Pawn };
-
对于 enum class,枚举值的名称不会自动导出到封闭作用域,所以不同的强类型枚举可以具有相同名称的枚举值
enum class State {Unknown,Started,Finished}; enum class Error {None,BadInput,DiskFull,Unknown}; //好处是可以提供短名称
但是,这也意味着您必须完全限定枚举值,或使用 using enum 或 using。
从 c++20 开始,可以使用 using enum 枚举声明来避免必须完全限定枚举值。如下:using enum PieceType; PieceType piece {king}; //using声明,King可以不用,其他枚举值仍需要完全限定 using PieceType::king; PieceType piece {king}; piece = PieceType::Queen;
16. Old-Style Enumerated Types 旧式枚举类型
在遗留的代码库中,会发现旧风格的枚举:enum 而不是 enum class。
enum PieceType { PieceTypeKing, PieceTypeQueen, PieceTypeRook, PieceTypePawn };
这种旧式枚举的值会被导出到封闭作用域,无需完全限定,例如:
PieceType myPiece { PieceTypeQueen };
这也意味着他们可能与父作用域中已经定义的其他名称发生冲突:
bool ok { false };
enum Status { error, ok };
//无法编译,Visual C++ 2019 报错
error C2365: 'ok': redefinition; previous definition was 'data variable'
17. Structs 结构化
-
Structs允许您将一个或多个现有类型封装到一个新的类型中。
模块接口文件通常的扩展名为.cppm。模块接口文件的第一行为模块声明。此外,需显式地说明它导出的内容。例如:export module employee; export struct Employee { char firstInitial; char lastInitial; int employeeNumber; int salary; };
-
main.cpp 注意引入的模块不能使用角标
import <iostream>; import <format>; import employee; using namespace std; int main() { // Create and populate an employee. Employee anEmployee{}; anEmployee.firstInitial = 'J'; anEmployee.lastInitial = 'D'; anEmployee.employeeNumber = 42; anEmployee.salary = 80000; // Output the values of an employee. cout << format("Employee: {}{}", anEmployee.firstInitial, anEmployee.lastInitial) << endl; cout << format("Number: {}", anEmployee.employeeNumber) << endl; cout << format("Salary: ${}", anEmployee.salary) << endl; }
18. Conditional Statements 条件语句
(1) if/else Statement
-
括号间的表达式必须是布尔值或计算为布尔值。true=1 false=0
-
if 中可以包含一个初始化器,在
<initialier>
中引入的变量只能在 if 语句中使用。//初始化程序获取一个员工,条件检索进而 判断主体中的表达式是否被执行 if (Employee employee { getEmployee() }; employee.salary > 1000) { ... }
(2) switch Statements 开关语句
-
在 c++ 中,开关语句的表达式必须是 integral 类型,可转换为 integral 类型, enumerated 类型或 strongly typed enumeration 的类型,并且必须与常量进行比较。
每个 constant value 代表一种 case,如果表达式与 case 匹配,执行后续 所有 代码,直到 break .switch (menuItem) { case OpenMenuItem: // Code to open a file break; case SaveMenuItem: // Code to save a file break; default: // Code to give an error message break; }
-
switch 和 if 语句可以互换:
if (menuItem == OpenMenuItem) { // Code to open a file } else if (menuItem == SaveMenuItem) { // Code to save a file } else { // Code to give an error message }
-
使用情况 : 当你想根据一个表达式的多个特定值来做事情,而不是对该表达式进行测试时
enum class Mode {Default,Custom,Standard}; int value {42}; Mode mode {/* */}; switch(mode){ using enum Mode; case Custom: value = 84; [[fallthrough]] //失败属性告知编译器,防止对自定义情况失败发出警告. case Standart; case Default; break; }
-
和 if 语句一样,可以在开关语句中使用初始化器
switch(<initializer>;<expression>){<body>}
19. The Conditional Operator 条件运算符
-
三元运算符 ? :
cout<<((i>2)?"yes":"no");
20. Logical Evaluation Operators 逻辑评价运算符
OP | DESCRIPTION | USAGE |
---|---|---|
<=> | 三向比较运算符 | result = i <=> 0; |
-
Three-Way Comparisons
用来确定两个值的顺序,不能返回布尔类型。它返回了一个 enumeration-like 类型,定义在<compare>
和std
命名空间中。
如果是整数型,则结果是强排序; less , greater , equal
如果是浮点类型,结果是部分排序; + unordered 代表其中一个或两个操作数都不是一个数字
还有一个弱排序。int i {11}; strong_ordering result { i <=> 0 }; //强排序 partial_ordering result {/** */} //部分排序 weak_ordering result {/** */} //弱排序 if (result == strong_ordering::less) { cout << "less" << endl; } if (result == strong_ordering::greater) { cout << "greater" << endl; } if (result == strong_ordering::equal) { cout << "equal" << endl; }
-
最后,
<compare>
提供了已命名的比较函数来解释排序的结果。示例:int i { 11 }; strong_ordering result { i <=> 0 }; if (is_lt(result)) { cout << "less" << endl; } if (is_gt(result)) { cout << "greater" << endl; } if (is_eq(result)) { cout << "equal" << endl; }
21. Functions 函数
(1) Function Return Type Deduction 函数返回类型演绎
//auto类型,自动识别返回类型
auto addNumbers(int number1, int number2)
{
return number1 + number2;
}
//该函数中的第一个返回语句必须是非递归调用
(2) Function Overloading 函数过载
-
重加载函数意味着提供多个具有相同名称但具有不同参数集的函数。
当调用addNumber()
时,编译器会根据所提供的参数自动选择正确的函数重载。int addNumbers(int a, int b) { return a + b; } double addNumbers(double a, double b) { return a + b; }
22. Attributes 属性
- 自C++11以来,通过使用双方括号语法
[[attribute]]
对属性有标准化的支持。
-
[[nodiset]]
属性可以用于返回一个值的函数,以便编译器在调用该函数时发出警告,而不对返回的值做一些操作。下面是一个示例:[[nodiscard]] int func() { return 42; } int main() { func(); } //warning C4834: discarding return value of function with 'nodiscard' attribute
此特性可以用于返回错误代码的函数。从C++20开始,可以提供字符串形式提供
[[丢弃]]
属性的原因,例如:[[nodiscard("Some explanation")]] int func();
-
[[maybe_used]]
属性可以用来抑制编译器在未使用时发出警告,如下例:int func(int param1, [[maybe_unused]] int param2) { return 42; }
-
[[noreturn]]
意味着它永远不会将控制权返回给调用站点。[[noreturn]] void forceProgramTermination() { std::exit(1); // Defined in <cstdlib> } bool isDongleAvailable() { bool isAvailable { false }; // 检查是否有一个授权加密狗可用... return isAvailable; } bool isFeatureLicensed(int featureId) { if (!isDongleAvailable()) { // 没有找到授权加密狗,中止程序执行! forceProgramTermination(); } else { bool isLicensed { featureId == 42 }; // 加密狗可用,对给定的功能进行许可证检查。... return isLicensed; } } int main() { bool isLicensed { isFeatureLicensed(42) }; } //如果不加[[noreturn]] //warning C4715: 'isFeatureLicensed': not all control paths return a value
-
[[deprecated]]
可以用来将某物标记为弃用,这意味着您仍然可以使用它,但不鼓励使用它。此属性接受一个可选参数,可用于解释废弃的原因,如本例所示:[[deprecated("Unsafe method, please use xyz")]] void func();
-
[[likely]]
and[[unlikely]]
优化代码,标记 if 分支,但不常用。
24. C-Style Arrays
-
在C++中,您必须在声明该数组时提供该数组的大小。不能指定变量作为大小,它必须是常量或常量表达式
//一些数组初始化的方式 int myArray[3] = {0}; int myArray[3] = {}; int myArray[3] {}; int myArray[] {1,2,3,4}; int myArray[2] {1}; //基于堆栈的 C-Style Arrays 的大小,在<cstddef>中定义的无符号整数类型 size_t arraySize { std::size(myArray) }; //旧的技巧 数组的字节大小/第一个元素的字节大小 size_t arraySize { sizeof(myArray) / sizeof(myArray[0]) }; //二维字符数组(字符类型) char ticTacToeBoard[3][3]; ticTacToeBoard[1][1] = 'o';
在c++中,尽量使用标准库功能,例如
std::array
和vector
.
25. std::array
-
define in
<array>
, std::array
优点:知道自己的大小,不会自动强制转换到指针,并且有迭代器(17章)来轻松的循环元素。array<int, 3> arr { 9, 8, 7 }; cout << format("Array size = {}", arr.size()) << endl; cout << format("2nd element = {}", arr[1]) << endl;
26. std::vector
-
define in
<vector>
存储信息的非固定大小容器// Create a vector of integers. vector<int> myVector { 11, 22 }; // Add some more integers to the vector using push_back(). myVector.push_back(33); myVector.push_back(44); // Access elements. cout << format("1st element: {}", myVector[0]) << endl;
向量和数组一样,需要用角括号来指定模板参数。
27. std::pair
-
define in
<utility>
,它将两个可能具有不同类型的值组合在一起。这些值可以通过第一个和第二个公共数据成员进行访问。下面是一个示例://支持CTAD pair<double, int> myPair { 1.23, 5 }; cout << format("{} {}", myPair.first, myPair.second);
28. std::optional
-
define in
<optional>
如果值是可选的,可用于函数的参数;如果函数可以返回或不返回某物,也经常被用来作函数的返回类型。
它还消除了将函数写为返回布尔值,表示成功或失败,同时将函数的实际结果存储在传递给函数的参数中作为输出参数。//类模板 optional<int> getData(bool giveIt) { if (giveIt) { return 42; } eturn nullopt; // or simply return {}; } //调用 optional<int> data1 { getData(true) }; optional<int> data2 { getData(false) }; //要判断可选是否具有值 has_value() cout << "data1.has_value = " << data1.has_value() << endl; //1=有值 if (data2) { cout << "data2 has a value." << endl; //不会输出,实质0=无值 } //有值的两种检索方式 cout<<data1.value()<<endl; //42 在空 optional 上会抛异常 cout<<*data1<<endl; //42 //value_or 当 value 为空时,返回另一个值 cout << "data2.value = " << data2.value_or(0) << endl;
注意:不能在
optional
存储reference
,如:optional<T&>
,相反可以在可选的指针中存储一个指针。
29. Structured Bindings 结构化绑定
-
允许声明多个变量。给变量赋数组的值?
//有如下数组 array values { 11, 22, 33 }; //对结构化绑定使用 auto 关键字,变量数与右侧表达式中的值的数量相匹配 auto [x, y, z] { values };
//当非静态成员都是公共的时候,也可以使用 struct Point { double m_x, m_y, m_z; }; Point point; point.m_x = 1.0; point.m_y = 2.0; point.m_z = 3.0; auto [x, y, z] { point };
pair myPair { "hello", 5 }; auto [theString, theInt] { myPair }; // 使用结构化绑定进行分解 cout << format("theString: {}", theString) << endl; cout << format("theInt: {}", theInt) << endl;
30. Loops 循环
-
while
- 表达式的计算值只要为真,就一直循环执行一个代码块。
-
do/while
- 如果希望一个代码块总是至少执行一次,并可能增加一些条件时。
-
for
-
The Range-Based for Loop
-
基于范围的 for 循环,遍历每个动态数组元素的副本并打印
array arr {1,2,3,4}; for(int i : arr ){cout<<i<<endl;} //迭代元素本身要用 vector
-
针对 The Range-Based for Loop 的初始化
从 c++20 开始,可以使用基于范围循环的初始化器,类似于 if 和 switch 的初始化器。(把初始化数组的步骤放到循环中)//初始化器中引入的变量只能在循环中使用 for (<initializer>; <for-range-declaration> : <for-range-initializer>) { <body> } //实例 for (array arr { 1, 2, 3, 4 }; int i : arr) { cout << i << endl; }
31. Initializer Lists
-
define in
<initializer_list>
,编写可以接受可变数量参数的函数。
std::initializer_list 是一个类模板,因此需要在尖括号之间的列表中指定对象的类型。//初始化 import <initializer_list>; using namespace std; int makeSum(initializer_list<int> values) { int total { 0 }; for (int value : values) { total += value; } return total; }
32. Strings in C++
- 有两种工作方法:
c风格:将字符串表示为字符数组
c++风格:以更易于使用和更安全的字符串类型包装c样式表示 - define in
<string>
字符串可以像字符数组一样使用
33. C++ as an Object-Oriented Language 面向对象语言
-
Defining Classes
- 该定义从声明类名开始。在一组花括号中,将声明该类的数据成员(属性)及其方法(行为)。每个数据成员和方法都与特定的访问级别相关联:公共的、受保护的或私人的。
export module airline_ticket; import <string>; export class AirlineTicket { public: AirlineTicket(); //构造函数 constructor ,创建类的对象时,会自动调用它。 ~AirlineTicket(); //析构函数 destructor ,当对象被销毁时,它会被自动调用 double calculatePriceInDollars(); std::string getPassengerName(); void setPassengerName(std::string name); int getNumberOfMiles(); void setNumberOfMiles(int miles); bool hasEliteSuperRewardsStatus(); void setHasEliteSuperRewardsStatus(bool status); private: //约定,在类的每个数据成员前缀为小写m和下划线 std::string m_passengerName; int m_numberOfMiles; bool m_hasEliteSuperRewardsStatus; };
- 初始化一个类中的数据成员。
//使用构造函数初始化器,函数名称后跟一个冒号。 AirlineTicket::AirlineTicket() : m_passengerName { "Unknown Passenger" } , m_numberOfMiles { 0 } , m_hasEliteSuperRewardsStatus { false } { } //将初始化放在构造函数的主体中 AirlineTicket::AirlineTicket() { // Initialize data members. m_passengerName = "Unknown Passenger"; m_numberOfMiles = 0; m_hasEliteSuperRewardsStatus = false; }
- 只初始化数据成员,实际上并不需要构造函数,因为可以类内初始化器
//修改类定义中的数据成员的定义,无需写机票构造函数 private: std::string m_passengerName { "Unknown Passenger" }; int m_numberOfMiles { 0 }; bool m_hasEliteSuperRewardsStatus { false };
- 如果您的类还需要执行一些其他类型的初始化,例如打开文件、分配内存等,那么您仍然需要编写一个构造函数来处理这些初始化。
//析构函数语法,执行一些清理操作 AirlineTicket::~AirlineTicket() { // Nothing to do in terms of cleanup }
- 其他一些机票类方法的定义:
double AirlineTicket::calculatePriceInDollars() { if (hasEliteSuperRewardsStatus()) { // 精英超级奖励客户免费飞行! return 0; } return getNumberOfMiles() * 0.1; } string AirlineTicket::getPassengerName() { return m_passengerName; } void AirlineTicket::setPassengerName(string name) { m_passengerName = name; } // Other get and set methods have a similar implementation.
- 直接放入到模块接口文件中
export class AirlineTicket { public: double calculatePriceInDollars(){ if (hasEliteSuperRewardsStatus()) { return 0; } return getNumberOfMiles() * 0.1; } std::string getPassengerName() { return m_passengerName; } void setPassengerName(std::string name) { m_passengerName = name; } int getNumberOfMiles() { return m_numberOfMiles; } void setNumberOfMiles(int miles) { m_numberOfMiles = miles; } bool hasEliteSuperRewardsStatus() { return m_hasEliteSuperRewardsStatus; } void setHasEliteSuperRewardsStatus(bool status){ m_hasEliteSuperRewardsStatus = status; } private: std::string m_passengerName { "Unknown Passenger" }; int m_numberOfMiles { 0 }; bool m_hasEliteSuperRewardsStatus { false }; }
-
Using Classes
34. Scope Resolution 范围解析
-
有时,作用域中的名称会隐藏其他作用域中相同的名称,或者作用域不是你想要的,可使用作用域解析操作符 ::
//具有get()方法的类、全局范围的get()函数、位于NS命名空间的get()函数 class Demo { public: int get() { return 5; } }; int get() { return 10; } namespace NS { int get() { return 20; } } //本例中,代码在main()中,处于全局范围内 int main() { Demo d; cout << d.get() << endl; // prints 5 cout << NS::get() << endl; // prints 20 cout << ::get() << endl; // prints 10 cout << get() << endl; // prints 10 } //如果NS的命名空间定义为 unnamed/anonymous 命名空间,会导致歧义名称解析的编译错误 cout<<get()<<endl; //在主函数下添加这个也会 using namespace NS;
35. Uniform Initialization 统一初始化
- 在 c++11 之前,类型的初始化并不总是一致的。例如:
struct CircleStruct
{
int x, y;
double radius;
};
class CircleClass
{
public:
CircleClass(int x, int y, double radius)
: m_x { x }, m_y { y }, m_radius { radius } {}
private:
int m_x, m_y;
double m_radius;
};
//c++11之前,结构体用花括号,类要用圆括号
CircleStruct myCircle1 = { 10, 10, 2.5 };
CircleClass myCircle2(10, 10, 2.5);
//c++11之后,可以都用花括号,而且并不限定于结构和类,
- 统一初始化的好处:防止缩小范围。旧式分配语法初始化变量时,c++隐式地执行缩小(gsl::narrow_cast()函数可以实现)
void func(int i) { /* ... */ }
int main()
{
int x = 3.14;
func(3.14);
}
//改用
int x {3.14};
func({3.14});
36. Designated Initializers 指定的初始化程序 c++20
- 语法结构,一个指定的初始化器以一个点开头,后跟一个数据成员的名称。
- 好处:
可以跳过某些成员的初始化,这是统一初始化语法做不到的。
当成员被添加到数据结构时,使用指定初始化器的现有代码将继续工作。新的数据成员将只用其默认值进行初始化。