C++ 11

auto

  • 使用 auto 必须初始化;
  • auto 根据初始化的值来推导数据类型;
  • 当类型不为引用时,auto 的推导结果将不保留表达式的 const 属性;
  • 当类型为引用时,auto 的推导结果将保留表达式的 const 属性。

限制

  1. 不能再函数参数中使用
  2. 不能作用于类的非静态成员变量(没有 static 关键字的变量)
  3. 不能定义数组;
  4. 不能用于模板参数

应用

  • 迭代器
  • 泛型编程

decltype

“declare type”的缩写。

和 auto 相似,用于自动的类型推导。

decltype(exp) varname = value;
根据 exp 推出类型,而不是 value

不要求变量初始化。

推导

  • exp 是变量,则类型为变量
  • exp 是函数,则类型为函数返回类型
  • exp 是左值,则类型为 exp 的引用

应用

  • 非静态成员

auto 和 decltype 区别

  • auto 可能不保留 const 等限定符,decltype 则会保留。
1
2
3
4
5
6
7
8
9
10
11
12
//非指针非引用类型
const int n1 = 0;
auto n2 = 10;
n2 = 99; //赋值不报错
decltype(n1) n3 = 20;
n3 = 5; //赋值报错
//指针类型
const int *p1 = &n1;
auto p2 = p1;
*p2 = 66; //赋值报错
decltype(p1) p3 = p1;
*p3 = 19; //赋值报错

对引用的处理

  • auto 抛弃引用类型,decltype 保留引用类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using namespace std;

int main() {
    int n = 10;
    int& r1 = n;
    std::cout << r1 << ", " << &r1 << std::endl; // 10, 0x61fe04
    std::cout << &n << std::endl; // 0x61fe04
    // auto推导
    auto r2 = r1;
    r2 = 20;
    cout << n << ", " << r1 << ", " << r2 << endl;
    // decltype推导
    decltype(r1) r3 = n;
    std::cout << &r3 << std::endl; //0x61fe04
    r3 = 99;
    cout << n << ", " << r1 << ", " << r3 << endl;
    return 0;
}

模板

深入理解模板


返回值类型后置


模板实例化中>> 符号的改进

在 C++ 98/03 中,>>符号通常被编译器解释为右移操作符,通常会提示在两个尖括号中间添加空格。

现在 C++ 11 对模板的 >> 符号进行了单独处理,不再出问题。

当然在需要右移操作符时,最好用括号将表达式括起来。


使用 using 代替 typedef 定义别名

1
2
3
4
5
6
7
8
template <typename Val>
struct str_map
{
typedef std::map<std::string, Val> type;
};
// ...
str_map<int>::type map1;
// ...

改成:

1
2
3
4
template <typename Val>
using str_map_t = std::map<std::string, Val>;
// ...
str_map_t<int> map1;
  • typedef 具有语法一致性,但遇到复杂的会降低可读性
  • using 直接在后面用赋值的方式,更清晰
1
2
3
4
5
6
7
8
9
10
11
12
13
/* 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;

使用 using 可以轻松地创建一个新的模板别名,而不需要像 C++98/03 那样使用烦琐的外敷模板。


支持函数模板的默认模板参数


在函数模板和类模板中使用可变参数

可变参数模板


tuple 元组详解


列表初始化


lambda 匿名函数

Lambda 表达式


非受限联合体(union)

1. C++11 允许非 POD 类型

POD 类型一般具有以下几种特征(包括 class、union 和 struct 等):

  1. 没有用户自定义的构造函数、析构函数、拷贝构造函数和移动构造函数。
  2. 不能包含虚函数和虚基类。
  3. 非静态成员必须声明为 public。
  4. 类中的第一个非静态成员的类型与其基类不同,例如:

class B1{};
class B2 : B1 {B1 b;};

class B2 的第一个非静态成员 b 是基类类型,所以它不是 POD 类型。

  1. 在类或者结构体继承时,满足以下两种情况之一:
  • 派生类中有非静态成员,且只有一个仅包含静态成员的基类;
  • 基类有非静态成员,而派生类没有非静态成员。
  1. 所有非静态数据成员均和其基类也符合上述规则(递归定义),也就是说 POD 类型不能包含非 POD 类型的数据。
  2. 此外,所有兼容 C 语言的数据类型都是 POD 类型(struct、union 等不能违背上述规则)。

2. C++11 允许联合体有静态成员

3. 注意

C++11 规定,如果非受限联合体内有一个非 POD 的成员,而该成员拥有自定义的构造函数,那么这个非受限联合体的默认构造函数将被编译器删除;其他的特殊成员函数,例如默认拷贝构造函数、拷贝赋值操作符以及析构函数等,也将被删除。

这条规则可能导致对象构造失败。

例子:

1
2
3
4
5
6
7
8
9
10
#include <string>
using namespace std;
union U {
string s; // string有自己的构造函数
int n;
};
int main() {
U u; // 构造失败,因为 U 的构造函数被删除
return 0;
}

解决问题要用到 placement new ,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <string>
using namespace std;
union U {
string s;
int n;
public:
U() { new(&s) string; }
~U() { s.~string(); }
};
int main() {
U u;
return 0;
}

placement new

语法:
new(address) ClassConstruct(…)

  • address 是内存地址。
  • ClassConstruct 表调用类的构造函数,如没有可省略括号

placement new 利用已经申请好的内存来生成对象,它不再为对象分配新的内存,而是将对象数据放在 address 指定的内存中。在本例中,placement new 使用的是 s 的内存空间。

非受限联合体的匿名声明和 “枚举式类”


for 循环(基于范围的循环)详解

C++ 11 :

1
2
3
for (declaration : expression){
// 循环体
}

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <vector>
using namespace std;
int main() {
char arc[] = "http://c.biancheng.net/cplus/11/";
//for循环遍历普通数组
for (char ch : arc) {
cout << ch;
}
cout << '!' << endl;
vector<char>myvector(arc, arc + 23);
//for循环遍历 vector 容器
for (auto ch : myvector) {
cout << ch;
}
cout << '!';
return 0;
}

注意事项

  • 不管是遍历什么,即使是遍历容器,也不是遍历指向各个元素的迭代器,而是容器中的各个元素。
  • 用范围的方式还可以遍历字符串
  • 不支持遍历指针形式返回的数组(需要范围明确)
  • 遍历 string 或容器时,遍历过程中函数只执行一次,如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <string>
using namespace std;
string str= "http://c.biancheng.net/cplus/11/";
string retStr() {
cout << "retStr:" << endl;
return str;
}
int main() {
//遍历函数返回的 string 字符串
for (char ch : retStr()) {
cout << ch;
}
// output:

// retStr:
//http://c.biancheng.net/cplus/11/
return 0;
}
  • 使用基于范围的循环时,不修改容器中不允许被修改的部分
  • 基于范围的循环,其底层也是借助容器迭代器实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <vector>
int main(void)
{
std::vector<int>arr = { 1, 2, 3, 4, 5 };
for (auto val : arr)
{
std::cout << val << std::endl;
arr.push_back(10); //向容器中添加元素
// 添加元素致使迭代器失效
}
// 输出:
//1
//-572662307
//-572662307
//4
//5
return 0;
}

long long 超长整型


右值引用


移动构造函数的功能和用法


move() 函数


引用限定符


完美转发及其实现


nullptr 初始化空指针


智能指针


作者

Erial

发布于

2022-10-18

更新于

2023-02-17

许可协议

评论