1. C++ 11是什么,C++ 11标准的由来
- Bjarne Stroustrup
- Simula
2. C++ auto类型推导
- auto声明推导多个变量时,不能有二义性
- 定义时必须马上初始化
- auto+const
- 当类型不为引用时,auto 的推导结果将不保留表达式的 const 属性;
- 当类型为引用时,auto 的推导结果将保留表达式的 const 属性。
- 限制:
- auto 不能在函数的参数中使用,定义函数的时候没有给它赋值
- auto 不能作用于类的非静态成员变量(也就是没有 static 关键字修饰的成员变量)中。
- auto 关键字不能定义数组
- auto 不能作用于模板参数
- 好处
- 迭代器
- 泛型编程
3. C++ decltype类型推导
-
decltype 是“declare type”的缩写,译为“声明类型”
-
decltype(exp) varname = value;
-
decltype不要求初始化
-
exp的值不能是void
-
如果 exp 是一个左值,或者被括号
( )
包围,那么 decltype(exp) 的类型就是 exp 的引用;假设 exp 的类型为 T,那么 decltype(exp) 的类型就是 T&decltype((obj.x)) b = a; //obj.x 带有括号,符合推导规则三,b 的类型为 int&。 decltype(n = n + m) d = c; //n=n+m 得到一个左值,符号推导规则三,所以推导结果为 int&
-
实际应用*:
decltype(T().begin())
4. 汇总auto和decltype的区别
- 对cv限定符的处理(const、volatile),volatile 和 const 是相反的,它用来表示数据是可变的、易变的,目的是不让 CPU 将数据缓存到寄存器,而是从原始的内存中读取
- 如果表达式的类型不是指针或者引用,auto 会把 cv 限定符直接抛弃,推导成 non-const 或者 non-volatile 类型。
- 如果表达式的类型是指针或者引用,auto 将保留 cv 限定符。
- decltype 会保留 cv 限定符
- 当表达式的类型为引用时,auto 和 decltype 的推导规则也不一样;decltype 会保留引用类型,而 auto 会抛弃引用类型,直接推导出它的原始类型
5. C++返回值类型后置
-
返回值类型后置语法,是为了解决函数返回值类型依赖于参数而导致难以确定返回值类型的问题
-
int& foo(int& i); float foo(float& f); template <typename T> auto func(T& val) -> decltype(foo(val)) { return foo(val); }
6. C++11对模板实例化中连续右尖括号>>的改进
- > >存在二义性
7. C++11使用using定义别名
-
template <typename Val> struct str_map { typedef std::map<std::string, Val> type; }; // ... str_map<int>::type map1; // ... template <typename Val> using str_map_t = std::map<std::string, Val>; // ... str_map_t<int> map1;
-
/* C++98/03 */ template <typename T> struct func_t { typedef void (*type)(T, T); }; // 使用 func_t 模板 func_t<int>::type xx_1; /* C++11 */ template <typename T> using func_t = void (*)(T, T); // 使用 func_t 模板 func_t<int> xx_2;
8. C++11支持函数模板的默认模板参数
- C++98/03,类模板参数可有默认参数,函数模板不能有默认参数
- C++11,均可,不要求把默认参数写在最后,编译器自动推导
9. C++11在函数模板和类模板中使用可变参数
-
参数包:容纳多个参数的可变参数
void fun(int count, ...)
- … 可变参数必须作为函数的最后一个参数,且一个函数最多只能拥有 1 个可变参数
- 可变参数的前面至少要有 1 个有名参数
- 当可变参数中包含 char 类型的参数时,va_arg 宏要以 int 类型的方式读取;当可变参数中包含 short 类型的参数时,va_arg 宏要以 double 类型的方式读取。
- … 可变参数的方法仅适用于函数参数,并不适用于模板参数
-
可变参数模板(C++11提供)
-
可变参数函数模板:
template<typename... T> //模板参数包 void vair_fun(T...args) { //函数参数包 //函数体 }
-
可变参数类模板:
template <typename... Types> class tuple; std:tuple<任意数量、任意类型的参数> tp0;
10. C++11 tuple元组
11. C++11列表初始化
-
之前{}只能用在一些特定的数据类型上,C++11新增:
int a {3}
-
列表初始化可以直接使用在函数的返回值
12.C++11 lambda匿名函数
-
[外部变量访问方式说明符] (参数) mutable noexcept/throw() -> 返回值类型 { 函数体; };
-
sort(num, num+4, [=](int x, int y) -> bool{ return x < y; } );
13.C++11非受限联合体(union)***
14.C++11 for循环(基于范围的循环)详解
-
//新语法格式的 for 循环还支持遍历用{}大括号初始化的列表 for(int num : {1, 2, 3, 4, 5}){ cout << num << " "; }
-
想改变内容用auto&
-
如果需要在遍历序列的过程中修改器内部元素的值,就必须定义引用形式的变量;反之,建议定义const &(常引用)形式的变量(避免了底层复制变量的过程,效率更高),也可以定义普通变量。
15.使用C++11标准的for循环,这些坑千万别踩!
-
:后还可以放置函数
string retStr() { return str; } for (char ch : retStr()) { cout << ch; } //返回指针的函数不可以,遍历范围并未明确指明
-
整个遍历过程中,函数只会执行一次
-
在使用基于范围的 for 循环遍历容器时,应避免在循环体中修改容器存储元素的个数,会使迭代器失效,出现错误
16.C++11 constexpr:验证是否为常量表达式
- 常量表达式,指的就是由多个**(≥1)**常量组成的表达式
- constexpr 关键字的功能是使指定的常量表达式获得在程序编译阶段计算出结果的能力,而不必等到程序运行阶段
- constexpr 可用于修饰普通变量、函数(包括模板函数)以及类的构造函数
- constexpr 修改普通变量时,变量必须经过初始化且初始值必须是一个常量表达式
- 修饰函数:
- 整个函数的函数体中,除了可以包含 using 指令、typedef 语句以及 static_assert 断言外,只能包含一条 return 返回语句
- 该函数必须有返回值,即函数的返回值类型不能是 void。
- 常量表达式函数在使用前,必须要有该函数的定义(仅有声明不可以)
- return 返回的表达式必须是常量表达式
- 修饰类的构造函数:
- struct/class不能直接用 constexpr 修饰
- onstexpr 修饰类的构造函数时,要求该构造函数的函数体必须为空,且采用初始化列表的方式为各个成员赋值时,必须使用常量表达式
- 修饰模板函数:
- 如果 constexpr 修饰的模板函数实例化结果不满足常量表达式函数的要求,则 constexpr 会被自动忽略,即该函数就等同于一个普通函数
17.C++11 constexpr和const的区别
- 即凡是表达**“只读”语义的场景都使用 const,表达“常量”**语义的场景都使用 constexpr
- “只读”和“不允许被修改”之间并没有必然的联系
- const 用于为修饰的变量添加“只读”属性;而 constexpr 关键字则用于指明其后是一个常量(或者常量表达式)
18.C++11 long long超长整形详解
19.C++11右值引用
- 左值、右值
- 右值引用必须立即进行初始化操作,且只能使用右值进行初始化
- 和常量左值引用不同的是,右值引用还可以对右值进行修改
- 移动语义、完美转发
20.C++11移动构造函数的功能和用法
- 右值引用主要用于实现移动(move)语义和完美转发
- 移动语义:以移动而非深拷贝的方式初始化含有指针成员的类对象
- 移动语义:指的就是将其他对象(通常是临时对象)拥有的内存资源“移为已用”
- 我们知道,非 const 右值引用只能操作右值,程序执行结果中产生的临时对象(例如函数返回值、lambda 表达式等)既无名称也无法获取其存储地址,所以属于右值。
- 当类中同时包含拷贝构造函数和移动构造函数时,如果使用临时对象初始化当前类的对象,编译器会优先调用移动构造函数来完成此操作。只有当类中没有合适的移动构造函数时,编译器才会退而求其次,调用拷贝构造函数。
- 默认情况下,左值初始化同类对象只能通过拷贝构造函数完成,如果想调用移动构造函数,则必须使用右值进行初始化。(std::move() 函数,可以将左值强制转换成对应的右值,进而使用移动构造函数)
21.C++11 move()函数:将左值强制转换为右值
- 注意,移动构造函数的调用时机是:用同类的右值对象初始化新对象
- move():将某个左值强制转化为右值
22.C++11引用限定符
-
void fun() & { } //限定调用该函数的对象必须是一个左值对象 void fun() && { } //限定调用该函数的对象必须是一个右值对象
-
C++11 标准规定,当引用限定符和const都在时候,const 必须位于引用限定符前面
-
注意,引用限定符不适用于静态成员函数和友元函数。
-
注意:
- 当 const && 修饰类的成员函数时,调用它的对象只能是右值对象;
- 当 const & 修饰类的成员函数时,调用它的对象既可以是左值对象,也可以是右值对象
23.C++11完美转发及其实现
- 完美转发:函数模板可以将自己的参数“完美”地转发给内部调用的其它函数(所谓完美,不仅能准确地转发参数的值,还能保证被转发参数的左、右值属性不变)
- C++11 标准中规定,通常情况下右值引用形式的参数只能接收右值,不能接收左值。
- 但对于函数模板中使用右值引用语法定义的参数来说,它不再遵守这一规定,既可以接收右值,也可以接收左值(此时的右值引用又被称为**“万能引用”**)
- 只需知道,在实现完美转发时,只要函数模板的参数类型为 T&&,则 C++ 可以自行准确地判定出实际传入的实参是左值还是右值
- 对于函数模板内部来说,形参既有名称又能寻址,因此它都是左值。那么如何才能将函数模板接收到的形参连同其左、右值属性,一起传递给被调用的函数呢?:模板函数 forword<T>()
- 实现完美转发:右值引用定义形参+调用传参时候用forword() 模板函数修饰参数
24.C++11 nullptr:初始化空指针
- NULL、0
- NULL 并不是 C++ 的关键字,它是 C++ 为我们事先定义好的一个宏:#define NULL 0
25.C++11 shared_ptr智能指针
-
C++ 智能指针底层是采用引用计数的方式实现的
-
定义位于头文件,并位于 std 命名空间中
-
shared_ptr智能指针的创建
std::shared_ptr<int> p1; //不传入任何实参 std::shared_ptr<int> p2(nullptr); //传入空指针 nullptr std::shared_ptr<int> p3(new int(10)); std::shared_ptr<int> p3 = std::make_shared<int>(10); //调用拷贝构造函数 std::shared_ptr<int> p4(p3);//或者 std::shared_ptr<int> p4 = p3; //调用移动构造函数 std::shared_ptr<int> p5(std::move(p4)); //或者 std::shared_ptr<int> p5 = std::move(p4);
-
同一普通指针不能同时为多个 shared_ptr 对象赋值,否则会导致程序发生异常
-
在初始化 shared_ptr 智能指针时,还可以自定义所指堆内存的释放规则,这样当堆内存的引用计数为 0 时,会优先调用我们自定义的释放规则。
//指定 default_delete 作为释放规则 std::shared_ptr<int> p6(new int[10], std::default_delete<int[]>()); //自定义释放规则 void deleteInt(int*p) { delete []p; } //初始化智能指针,并自定义释放规则 std::shared_ptr<int> p7(new int[10], deleteInt); std::shared_ptr<int> p7(new int[10], [](int* p) {delete[]p; });
26.C++11 unique_ptr智能指针
-
和 shared_ptr 指针最大的不同之处在于,unique_ptr 指针指向的堆内存无法同其它 unique_ptr 共享
-
每个 unique_ptr 指针都独自拥有对其所指堆内存空间的所有权
-
unique_ptr 模板类没有提供拷贝构造函数,只提供了移动构造函数
std::unique_ptr<int> p5(p4);//错误,堆内存不共享 std::unique_ptr<int> p5(std::move(p4));//正确,调用移动构造函数 对于调用移动构造函数的 p4 和 p5 来说,p5 将获取 p4 所指堆空间的所有权,而 p4 将变成空指针(nullptr)
-
默认情况下,unique_ptr 指针采用 std::default_delete<T>方法释放堆内存。当然,我们也可以自定义符合实际场景的释放规则。值得一提的是,和 shared_ptr 指针不同,为 unique_ptr 自定义释放规则,只能采用函数对象的方式。
//自定义的释放规则 struct myDel { void operator()(int *p) { delete p; } }; std::unique_ptr<int, myDel> p6(new int); //std::unique_ptr<int, myDel> p6(new int, myDel());
27.C++11 weak_ptr智能指针
- weak_ptr类型指针通常不单独使用(没有实际用处),只能和 shared_ptr 类型指针搭配使用
- weak_ptr 类型指针并不会影响所指堆内存空间的引用计数