跳转至

简单资料参考

课本可以当作入门了解编程知识,但一般书的代码不符合现代C++的编程风格和设计,所以质量不佳,不建议模仿。至于比较好的C++学习资料,可以参考The Definitive C++ Book Guide and List

除了这些,在有了一定基础后,最重要的C++参考还是cppreference,如果觉得访问较慢,可以在首页下方下载离线版本。

进阶

C++11到C++17提供了各种丰富的语言特性,正确使用下可以避免写出可读性差而难以维护的代码。下面介绍一些C++编程中一些重要的,但是课堂通常不会涉及较多的特性或者说概念。

Stanrd Library

C++和其他很多语言同样,有自己的标准库。学会如何正确使用标准库,而不是再造轮子也是非常重要的。其中Standard Template Library(标准模板库STL)提供了算法和容器的模板。这些标准库的内容可以cppreference中查到。

未定义行为

未定义行为(undefined behavior,UB)——对程序的行为无任何限制。未定义行为的例子是数组边界外的内存访问,有符号整数溢出,空指针的解引用,在表达式中对同一标量多于一次的中间无序列点 (C++11 前)无序 (C++11 起)的修改,通过不同类型的指针访问对象,等等。不要求编译器诊断未定义行为(尽管许多简单情形确实会得到诊断),而且不要求所编译的程序做任何有意义的事

——cpp reference

未定义行为下,程序可以做出任何举动。在写代码的时候应该要考虑到当前的行为是不是未定义,即使运行正确,也只是一种巧合。常见新手写出来的未定义行为是有符号整数的一些操作,比如移位:

C++20前:

对于有符号的非负 a,若 a * 2b能以返回类型的无符号版本表示,则将该值转换到有符号后即是 a << b 的值(这使得以 1<<31 创建 INT_MIN 合法);否则行为未定义。

对于负 a,a << b 的行为未定义。

对于无符号 a 和有符号的非负 a,a >> b 的值是 a/2b 的整数部分。

对于负 a,a >> b 的值是实现定义的(大多数平台上进行算术右移,故结果保留为负)。

——cpp reference

通常情况下编译器会在静态分析时给未定义行为抛出警告(ReSharper也是),注意这些警告能避免这些问题。

我们应该感谢你的未定义程序运行时没有把你的硬盘格式化或者毁灭地球

——匿名

类似的行为还有实现定义的行为,指这个行为由编译器内部实现决定。比如CHAR_BIT的大小(即一字节的位数),尽管通常来说一个字节代表8位,但是这个数值标准中并没有给出定义,而是编译器实现的。

命名空间

不少人在代码开始的时候都是using nzamespace std;,初学编程的人可能没法意识到这个是什么意思,实际上是代表使用std(标准库)的命名空间。

命名空间,顾名思义,这是一个有名字的空间,是为了解决命名冲突的问题。想想你的项目越来越大,创建类越来越多,为了保证命名有意义且方便使用,就不可避免长命名。

class i_don't_know_how_to_name_this_but_it_does_exist;

而命名空间的作用,像是把命名分割一样。

代码在命名空间project::utility下定义了一个类tree。

在无命名空间下使用

project::utility::tree my_tree;

namespacce project下使用

namespace project
{
    void foo()
    {
        utility::tree my_tree;
        ...
    }
}

通过命名空间使用声明

using namespace project::utility;
tree my_tree;

那么回到一开始的using namespace std;这条语句,实际上是直接使用std命名空间中东西,而且不需要加前缀std::。在小项目中可能不会产生什么后果,而如果在大型项目中有可能定义了和std命名空间中同名的类、函数或者常量,在使用时不明确指代,这就是命名空间污染。

在工程实践中,一般会把多个类定义分到不同的文件中,并把不同命名空间放到不同的文件夹中,以保持项目有序。

常量表达式

这是C++与其他语言区别的一个重要特性,常量表达式和常量不同,常量可能是运行时执行初始化,而常量表达式要求值能在编译时求出来。这给了我们新的方式定义常量,不是

#define PI 3.1415926535

而是

constexpr auto pi = 3.1415926535;

这个关键词也可以用在函数修饰符上,此时这个函数既可以在编译时运行,也能用在运行时,取决于使用情况。

constexpr auto f(const int i){ return i + 1; }
...
std::cin >> i;
//constexpr auto my_value = f(i); ERROR, because i is not constexpr
constexpr auto my_compile_time_value = f(0);
const auto my_run_time_value = f(i);

​ 常量表达式除了可以是基础类型的编译时常量,constexpr修饰的函数或成员函数,也可以是其他符合一定条件的类型,即class或者struct定义的类型。这个特性可以使得一些代码仅在编译时施行,而在运行时不消耗时间,起到很好的优化作用。

........

其他重要参考

原文传送门

评论