Skip to content

Latest commit

 

History

History
166 lines (84 loc) · 6.7 KB

modern-cpp.md

File metadata and controls

166 lines (84 loc) · 6.7 KB

modern cpp

迈入现代C++

被弃用的特性

char* str =“hello world” 应该改用auto或者加const;

与C的兼容性

脑子应该树立C++不是C的一个超集概念。

避免使用void*之类的风格。就跟避免使用NULL一样,在c++中编译器会把这个类型看成int类型,导致函数重载的时候出现问题。

在不得不使用c的时候使用extern“c”这种特性,并且与c++代码分离编译在统一链接。

语言可用性的强化

语言可用性:是指那些发生在运行时之前的语言行为。这个功能是非常强大的,其实是编译器非常的牛逼,我记得用关键字就可以让一些运算在编译中计算好,运行性能大大增强。

常量

nullptr

nullptr 出现的目的是为了替代NULL。因为传统C++ 会把NULL、0 视为同一种东西,这取决于编译器如何定义NULL,这会导致C++ 中重载特性发生混乱。

c++不允许直接将void*隐式转换到其他类型。这也是避免使用void*风格的原因之一。

nullptr 的类型为nullptr_t,能够隐式的转换为任何指针或成员指针的类型。

constexpr

c++具备了常量表达式的概念。

如果编译器能够在编译时就把“1+2, 3*4“”这些表达式直接优化并植入到程序运行时,将能增加程序的性能。

const int len_2 = len + 1; / /char arr_4[len_2];; // 非法 因为len2被定义为const常数,而不是一个常量表达式,所以(即便这种行为在大部分编译器中都支持,但是)它是一个非法的行为。

很多非法行为在编译器优化的加持下会变得合法。

C++11 提供了constexpr 让用户显式的声明函数或对象构造函数在编译期会成为常量表达式。

constexpr int len_2_constexpr = 1 + 2 + 3; char arr_4[len_2_constexpr]; //**合法 1+2+3这个运算在编译期间完成运算, 不用再运行期间计算,**这也是上面说的语言的可用性强化。

变量及其初始化

if/switch 变量声明强化

// 将临时变量放到 if 语句内

if (const std::vector::iterator itr = std::find(vec.begin(), vec.end(), 3);

之前不能再if中定义这个变量导致itr 这一变量是定义在整个main() 的作用域内

初始化列表

对于类对象的初始化,要么需要通过拷贝构造、要么就需要使用() 进行,不能用{}进行初始化。

C++11 首先把初始化列表的概念绑定到了类型上,并将其称之为std::initializer_list,允许构造函数或其他函数像参数一样使用初始化列表,这就为类对象的初始化与普通数组和POD 的初 始化方法提供了统一的桥梁。

类型推导

C++11 引入了auto 和decltype 这两个关键字实现了类型推导,让编译器来操心变量的类型。

在传统C++ 中,如果一个变量没有声明为register 变量,将自动被视为一个auto 变量。

注意:auto 还不能用于推导数组类型,auto 不能用于函数形参进行类型推导。

decltype:decltype(表达式) ,decltype 用于推断类型的用法。

decltype(auto)

ecltype(auto) 主要用于对转发函数或封装的返回类型进行推导,它使我们无需显式的指定decltype 的参数表达式。

decltype(auto) look_up_a_string_1() {
return lookup1();
}

控制流

if constexpr

constexpr 关键字将表达式或函数编译为常量结果。

C++17 将constexpr 这个关键字引入到if 语句中,允许在代码中声明常量表达式的判断条件。

区间for 迭代

for (auto element : vec)

模板

模板的哲学在于将一切能够在编译期处理的问题丢到编译期进行处理,仅在运行时处理那些最核心的动态服务,进而大幅优化运行期的性能。

外部模板

传统C++ 中,模板只有在使用时就会被编译器实例化。这就产生了重复实例化而导致的编译时间的增加。

C++11 引入了外部模板,使我们能够显示通知编译器何时进行模板的实例化。

template class std::vector; // 强行实例化

extern template class std::vector; // 不在该当前编译文件中实例化模板

类型别名模板

理解『模板』和『类型』之间的不同。

体会这句话:模板是用来产生类型的。

**** 在传统C++ 中,typedef 可以为类型定义一个新的名称,但是却没有办法为模板定义一个新的名称。因为,模板不是类型。

C++11 使用using 引入了下面这种形式的写法,具有typedef相同的功效。using更好阅读。

typedef int (*process)(void *); //函数指针

using NewProcess = int(*)(void *);

变长参数模板

之前只能接受固定数量的模板参数。而C++11 加入了新的表示方法,允许任意个数、任意类别的模板参数,同时也不需要在定义时将参数的个数固定。

面向对象

委托构造

C++11 引入了委托构造,使得构造函数可以在同一个类中一个构造函数调用另一个构造函数,从而达到简化代码的目的

继承构造

在传统C++ 中,构造函数如果需要继承是需要将参数一一传递的,这将导致效率低下。C++11 利用关键字using 引入了继承构造函数的概念

显示虚函数重载

在传统C++ 中,经常容易发生意外重载虚函数的事情。

并不是程序员尝试重载虚函数,只是恰好加入了一个具有相同名字的函数。

当基类的虚函数被删除后,子类拥有旧的函数就不再重载该虚拟函数并摇身一变成为了一个普通的类方法,这将造成灾难性的后果。

C++11 引入了override 和final 这两个关键字来防止上述情形的发生。

override:当重载虚函数时,引入override 关键字将显式的告知编译器进行重载,编译器将检查基函数是否存在这样的虚函数

final:final 则是为了防止类被继续继承以及终止虚函数继续重载引入的。

显式禁用默认函数

Magic() = default; // 显式声明使用编译器生成的构造

Magic& operator=(const Magic&) = delete; // 显式声明拒绝编译器生成构造

传统c++一般禁用构造是将其声明为private。

强类型枚举

在传统c++同一个命名空间中的不同枚举类型的枚举值名字不能相同,因为它的作用域就是整个空间。

c++11 引入了枚举类(enumeration class),并使用enum class 的语法进行声明。

这样定义的枚举实现了类型安全,首先他不能够被隐式的转换为整数,同时也不能够将其与整数数字进行比较,更不可能对不同的枚举类型的枚举值进行比较