鱼C论坛

 找回密码
 立即注册
查看: 2470|回复: 0

[技术交流] 「C++高级编程笔记」01 基础语法与类型的用法

[复制链接]
发表于 2021-2-27 09:13:15 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 fatsky 于 2021-2-27 09:13 编辑

虽然学习了C++的基础语法对于自己写代码来说还算够用,但是经常看不懂别人写的代码,故开始啃《C++高级编程》一书,这里记一些笔记,主要记录一些我不太熟悉的用法。
类型
强枚举类型
C语言中的enum默认是类型不安全的,枚举值可能超出值的范围。C++中引入了强枚举类型,形式如下:
enum class PieceType
{
    King = 1,
    Queen,
    Rook = 10,
    Pawn
};
enum class枚举值不会自动超出封闭的作用域,因此总要使用作用域解析操作符:
PieceType piece = PieceType::King;
另外,枚举值不会自动转换成整数,因此下面的代码是不合法的:
if (PieceType.Queen == 2) { ... }        // 非法代码
可以采用以下方式改变枚举值的基本类型:
enum class PieceType : unsigned long
{
    King = 1,
    Queen,
    Rook = 10,
    Pawn
};
if语句的初始化器
从C++17开始,允许在if语句中使用初始化器,语法如下:
if (<initializer> ; <conditional_expression>) { <body> }
其中,<initializer>中引入的变量只在<body>中有效。
就像for循环的用法。
switch语句的[fallthrough]
switch语句中忘记break可能会成为bug的来源,因此编译器大多会弹出警告。可以使用[[fallthrouth]]告诉编译器此处是故意而为之:
switch (backgroundColor)
{
    case Color::DarkBlue:
        doSomethingForDarkBlue();
        [[fallthrough]]
    case Color::Black
        doSomethingForBlack();
        break;
    ...
}
switch语句的初始化器
和if语句同理,在C++17中,switch语句也支持初始化器。
函数
自动推断
C++14允许自动推断函数的返回类型:
auto AddNumber(int number1, int number2) { return number1 + number2; }
若函数中存在多处返回,则它们的返回类型必须相同。
这个用法也支持递归调用,但是递归调用的第一个return不能是递归调用。
预定义变量__func__
每个函数都有一个预定义的局部变量__func__,它包含当前函数的名称。这个变量的一个用途是用于日志记录:
int AddNumbers(int number1, int number2)
{
    std::cout << "Entering Function " << __func__ << std::endl;
    return number1 + number2;
}
std::size()
在C++17中,可以直接使用std::size()来获取一个数组的容量。std::size()被包含在<array>头文件中。
在C++17之前,需要使用sizeof()来获取数组的大小,单位是byte。要获取数组容量需要这样做:
unsigned int arraySize = sizeof(array) / sizeof(array[0]);
使用std::size()就可以这样写了:
#include <array>
unsigned int arraySize = std::size(array);
但是针对二维数组,它只会返回第一维的大小:
int a[3][4];
unsigned int arraySize = std::size(a);        // arraySize = 3;
unsigned int arraySize2 = std::size(a[0]);        // arraySize2 = 4
std::array
在C++中,比起C风格的数组,更建议使用std::array。std::array基本上是对C风格数组的简单包装。
std::array比起C风格的数组有诸多好处,比如它总是知道自身大小,不会自动转换成指针,具有迭代器等。
其简单的应用如下:
std::array<int, 3> arr = {9, 8, 7};
std::cout << "Array size = " << arr.size() << std::endl;
std::cout << "second element = " << arr[1] << std::endl;
定义array时必须指定两个参数,第一个是类型,第二个是数组大小。std::array的大小必须是编译时确定的。动态数组应当使用std::vector。
结构化绑定
C++17引入了结构化绑定,这允许我们使用std::array、结构体、std::pair和元组来初始化。
例如:
std::array<int, 3> values = {1, 2, 3};
auto [x, y, z] = values;
// x = 1, y = 2, z = 3
但是这里注意,中括号里面的元素数量必须与数组的元素数量相等,否则编译时会报错。
结构体的用法如下:
struct Point {
    double mX, mY, mZ
};
Point point;
point.mX = 1.0; point.mY = 2.0; point.mZ = 3.0;
auto [x, y, z] = point;
基于区间的for循环
感觉用法比较像其他语言里的foreach语句。示例如下:
std::array<int, 4> arr = {1, 2, 3, 4};
for (int i : arr) {
    std::cout << i << std::endl;
}
// 分别输出4行,依次输出1 2 3 4
initializer_list
initializer_list有点像const的数组,它封装了一些方法,并且定义后其中的元素不能被改变。不支持直接使用[]访问元素。
使用时需要#include <initializer_list>:
#include <initializer_list>

using namespace std;

int makeSum(initializer_list<int> lst)
{
    int total = 0;
    for (int value : lst)
        total += value;
    return total;
}
可以使用如下方式调用makeSum:
int a = makeSum({1, 2, 3});
int b = makeSum({10, 20, 30, 40, 50, 60});
initializer_list是类型安全的。因此,在此处传入的数字必须都是整数。尝试传入double时,会导致编译器产生错误。
一点有趣的发现
我们尝试使用auto这样写的时候:
auto a = {1, 2, 3, 4, 5};
a会被定义成initializer_list,而不是数组。因此,对于上面的函数,我们这样写也是完全没有问题的:
auto a = {1, 2, 3, 4, 5};
cout << makeSum(a) << endl;
虽然数组也可以这样定义,但是向makeSum函数传入数组会导致编译错误。
最后,有人知道fishc支持markdown吗?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-9-22 13:44

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表