Understanding C++ 11

C++11的问题若干

历史遗留问题

被弃用的特性

  • 如果一个类有析构函数,为其生成拷贝构造函数和拷贝赋值运算符的特性被弃用了。
  • 不再允许字符串字面值常量赋值给一个 char *。如果需要用字符串字面值常量赋值和初始化一个 char *,应该使用 const char * 或者 auto。 e.g.

    1
    char *str = "hello world!"; // 将出现弃用警告
  • C++98 异常说明、 unexcepted_handler、set_unexpected() 等相关特性被弃用,应该使用 noexcept。

  • auto_ptr 被弃用,应使用 unique_ptr。
  • register 关键字被弃用。
  • bool 类型的 ++ 操作被弃用。
  • C 语言风格的类型转换被弃用,应该使用。 static_castreinterpret_castconst_cast 来进行类型转换。
  • 还有一些其他诸如参数绑定(C++11 提供了 std::bind 和 std::function)、export 等特性也均被弃用。

C++ 不是 C 的一个超集

在编写 C++ 时,也应该尽可能的避免使用诸如 void* 之类的程序风格。而在不得不使用 C 时,应该注意使用 extern “C” 这种特性,将 C 语言的代码与 C++代码进行分离编译,再统一链接这种做法。 e.g.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// foo.h
#ifdef __cplusplus
extern "C" {
#endif

int add(int x, int y);

#ifdef __cplusplus
}
#endif

// foo.c
int add(int x, int y) {
return x+y;
}

// main.cpp
#include "foo.h"
int main() {
add(1, 2);
return 0;
}

要先编译c代码, 然后再把c++和.o代码链接起来

1
2
gcc -c foo.c
g++ main.cpp foo.o -o main

语言特性增强

nullptr and constexpr

nullptr

  • nullptr 出现的目的是为了替代 NULL。在某种意义上来说,传统 C++ 会把 NULL0 视为同一种东西,这取决于编译器如何定义 NULL,有些编译器会将 NULL 定义为 ((void*)0),有些则会直接将其定义为 0

  • C++ 不允许直接将 void * 隐式转换到其他类型

  • C++11 引入了 nullptr 关键字,专门用来区分空指针0nullptr 的类型为 nullptr_t,能够隐式的转换为任何指针或成员指针的类型,也能和他们进行相等或者不等的比较。 防止NULL转换撑0, 进而在重载过程中匹配成int

constexpr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#define LEN 10

int len_foo() {
return 5;
}

int main() {
char arr_1[10];
char arr_2[LEN];
int len = 5;
char arr_3[len+5]; // 非法
const int len_2 = 10;
char arr_4[len_2]; // 合法
char arr_5[len_foo()+5]; // 非法

return 0;
}

C++11 之前,可以在常量表达式中使用的变量必须被声明为 const,在上面代码中,len_2 被定义成了常量,因此 len+5 是一个常量表达式,所以能够合法的分配一个数组;
而对于 arr_5 来说,C++98 之前的编译器无法得知 len_foo() 在运行期实际上是返回一个常数,这也就导致了非法的产生。
C++11 提供了 constexpr 让用户显式的声明函数或对象构造函数在编译器会成为常数,这个关键字明确的告诉编译器应该去验证 len_foo 在编译器就应该是一个常数。也就是说, 相当于函数返回的值告诉编译器也是一个const, 可作为数组的长度定义。
同时constexpr的函数还支持递归, e.g.

1
2
3
constexpr int fibonacci(const int n) {
return n == 1 || n == 2 ? 1 : fibonacci(n-1)+fibonacci(n-2);
}

Deduction

auto

auto 在很早以前就已经进入了 C++,但始终作为一个存储类型的指示符存在,与 register 并存。在传统 C++ 中,如果一个变量没有声明为 register,将自动被视为一个 auto。然而register 被弃了,auto 的语义变更也就非常自然了。

常见用法
1
2
auto i = 5;             // i 被推导为 int
auto arr = new auto(10) // arr 被推导为 int *

但是, auto 不能用于函数传参,因此下面的做法是无法通过编译的(考虑重载的问题,我们应该使用模板); 此外,auto 还不能用于推导数组类型

auto应用于迭代器

三者遍历方式等价, 只是最后一种auto的是元素本身类型, 不是迭代器本身

1
2
3
4
5
for(vector<int>::const_iterator itr = vec.cbegin(); itr != vec.cend(); ++itr)

for(auto itr = vec.cbegin(); itr != vec.cend(); ++itr);

for(auto item: vec)

decltype

decltype 关键字是为了解决 auto 关键字只能对变量进行类型推导的缺陷而出现的, 用法类似sizeof. e.g.计算某个表达式的类型:

1
2
3
auto x = 1;
auto y = 2;
decltype(x+y) z;

Contents
  1. 1. C++11的问题若干
    1. 1.1. 历史遗留问题
      1. 1.1.1. 被弃用的特性
      2. 1.1.2. C++ 不是 C 的一个超集
    2. 1.2. 语言特性增强
    3. 1.3. nullptr and constexpr
      1. 1.3.0.1. nullptr
      2. 1.3.0.2. constexpr
    4. 1.3.1. Deduction
      1. 1.3.1.1. auto
        1. 1.3.1.1.1. 常见用法
        2. 1.3.1.1.2. auto应用于迭代器
      2. 1.3.1.2. decltype