C++2.0

C++11 新特性

2018-08-12  本文已影响34人  xiongzenghui

0、C++编译环境版本判断

1. C++0x

#define __cplusplus 1
#define __GXX_EXPERIMENTAL_CXX0X__ 1

2. C++11

#define __cplusplus 201103L
#define __GXX_EXPERIMENTAL_CXX0X__ 1

3. C++14

#define __cplusplus 201402L
#define __GXX_EXPERIMENTAL_CXX0X__ 1

4. C++17

#define __cplusplus 201703L
#define __GXX_EXPERIMENTAL_CXX0X__ 1

5. 使用示例

比如判断当前是否是c++17的编译环境

#if defined(__GXX_EXPERIMENTAL_CXX0X__) && (__cplusplus >= 201703L)
  //【C++17】要编译的C++代码
#else
  //【非C++17】要编译的C++代码
#endif

1、右值引用 与 std::move() 避免右值对象拷贝

1. 右值

struct dog{};

// 返回值为一个临时Dog对象
dog get_dog()
{
  return dog();
}

2. 右值引用

1. C98标准

void func(const T& ref); 

2. C++11标准

void func(T&& ref);

3. 左值转为右值

struct dog{};

int main()
{
  dog d; // 左值
  dog&& ref = std::move(d); 
}

4. moveable 对象

struct dog
{
/**---------------- 左值 ------------------*/
  dog(const dog& d);  
  dog& operator=(const dog& d); 
  
  
/**---------------- 右值 ------------------*/
  dog(dog&& d);    
  dog& operator=(dog&& d);
};

5. 不应该返回T&&引用类型的对象

#include <iostream>
using namespace std;

struct dog{};

dog&& func()
{
  return dog();
}

int main()
{}
->  make lan=c++ ver=c++11
g++ main.cpp  -std=c++11
main.cpp:8:10: warning: returning reference to local temporary object [-Wreturn-stack-address]
  return dog();
         ^~~~~
1 warning generated.
./a.out
->

与返回局部栈帧上内存的引用报错原因一致。

2、初始化列表

1. 初始化列表构造数组、容器

[图片上传失败...(image-d61c4e-1534051110313)]

2. 构造函数初始化参数列表

[图片上传失败...(image-b86c9b-1534051110314)]

3. 函数参数

[图片上传失败...(image-3e6bf4-1534051110314)]

3、explicit

[图片上传失败...(image-c83c3a-1534051110314)]

[图片上传失败...(image-21d17b-1534051110314)]

4、auto

1. auto 自动推导数据的类型

[图片上传失败...(image-1e2270-1534051110314)]

2. 值的类型推导(C++11)

[图片上传失败...(image-403ae1-1534051110314)]

3. 推导返回值的类型(C++14)

[图片上传失败...(image-b6e658-1534051110314)]

4. auto 定义变量时==必须同时初始化==(类似引用)

#include <iostream>
int main()
{
  auto x; // error
  auto y = 10; // ok
}

编译错误

➜  main make lan=c++ ver=c++11
g++ main.cpp  -std=c++11
main.cpp:4:8: error: declaration of variable 'x' with type 'auto' requires an initializer
  auto x;
       ^
1 error generated.
make: *** [all] Error 1
➜  main

requires an initializer auto x;

5. 不允许使用auto定义==函数参数==

#include <iostream>

void func(auto x)
{}

int main()
{}

编译错误

➜  main make lan=c++ ver=c++11
g++ main.cpp  -std=c++11
main.cpp:3:11: error: 'auto' not allowed in function prototype
void func(auto x)
          ^~~~
1 error generated.
make: *** [all] Error 1
➜  main

6. 不允许使用auto定义==struct/class的成员变量==

#include <iostream>

struct animal
{
  int age;
  auto name; // error: 无法确定最终外层struct的占用长度
};

int main()
{}

编译错误

➜  main make lan=c++ ver=c++11
g++ main.cpp  -std=c++11
main.cpp:6:3: error: 'auto' not allowed in non-static struct member
  auto name;
  ^~~~
1 error generated.
make: *** [all] Error 1
➜  main

7. 不允许使用auto定义==数组==

#include <iostream>

int main()
{
  auto arr[5]; // error: 同样无法确定数组的总占用长度
}

编译错误

➜  main make lan=c++ ver=c++11
g++ main.cpp  -std=c++11
main.cpp:5:11: error: 'arr' declared as array of 'auto'
  auto arr[5];
          ^
1 error generated.
make: *** [all] Error 1
➜  main

8. 不允许使用auto作为==模板参数==传递

#include <iostream>
#include <vector>

int main()
{
  std::vector<int> v1; // ok
  std::vector<auto> v2; // error
}

编译错误

➜  main make lan=c++ ver=c++11
g++ main.cpp  -std=c++11
main.cpp:7:15: error: 'auto' not allowed in template argument
  std::vector<auto> v2; // error
              ^~~~
main.cpp:7:21: error: C++ requires a type specifier for all declarations
  std::vector<auto> v2; // error
                    ^
2 errors generated.
make: *** [all] Error 1
➜  main

9. auto 总结

5、decltype(decalare type)获取变量的类型

1. 用来获取变量的类型

[图片上传失败...(image-608891-1534051110314)]

#include <iostream>
#include <vector>

int main()
{
  int x = 5;
  float y = 5.1;

  decltype(x)& ref1 = x; // int& 
  decltype(y)& ref2 = y; // float&
  decltype(&x) ptr = &x; // int*
  decltype(x+y) val = x+y; // int+float => float
}

2. 推导函数对象的类型

decltype(std::less<int>()) func;

3. 推导函数的返回值类型

#include <iostream>
using namespace std;

// 指定函数的返回值类型、函数形参类型
decltype(0.0) func(decltype(0.0) x)
{
    return x;
}

// 定义函数指针1
typedef decltype(0.0) (*func_p_1)(decltype(0.0));

// 定义函数指针2
typedef decltype(func)* func_p_2;

int main(int argc, const char * argv[])
{
    func_p_1 m1 = func;
    func_p_2 m2 = func;
}

4. 推导容器元素的类型

#include <iostream>
#include <vector>
using namespace std;

int main(int argc, const char * argv[])
{
  std::vector<char> v;
  decltype(v)::iterator it;
}

5. 推导为【值】类型与【引用】类型

推导为值或引用的规则

[图片上传失败...(image-76361c-1534051110314)]

使用示例

[图片上传失败...(image-475b29-1534051110314)]

6. C++14 decltype(auto)

[图片上传失败...(image-dd20fa-1534051110314)]

7. 新式函数声明

1. 推导【函数参数】为返回值类型

auto func(int x) -> decltype(x)
{
  return x+1;
}

2. 推导【模板参数】为返回值类型

[图片上传失败...(image-48854a-1534051110314)]

6、nullptr

ansic(标准)c/c++ 语言中的空指针

#define NULL 0

c++11中的增强的空指针

int* p = nullptr;

nullptr 与 nil 等价

#include <iostream>
using namespace std;

int main(int argc, const char * argv[])
{
  // 1. 
  std::nullptr_t nil;

  // 2.
  if (nullptr == nil)
    cout << "相等" << endl;
}
->  g++ main.cpp
->  ./a.out
相等
->

7、default 启用编译器提供的默认函数实现

对于一些没有重写的函数实现,告诉编译器使用父类的函数实现

#include <iostream>
using namespace std;

class Dog
{
public:
  // 1. 构造与析构
  Dog() = default;
  ~Dog() = default;

  // 2. 构造拷贝与赋值拷贝
  Dog(const Dog& d) = default;
  Dog& operator=(const Dog& d) = default;
  
  // 3.【转移】形式的构造拷贝与赋值拷贝
  Dog(Dog&& d) = default;
  Dog& operator=(Dog&& d) = default;
};

int main(int argc, const char * argv[])
{}
->  g++ main.cpp -std=c++11
->  ./a.out
->

8、delete 禁用编译器默认生成的方法实现

1. C++11以下禁止生成默认方法实现

class Animal {
public:
  Animal(){cout << "Animal()" << endl;}
  ~Animal() {cout << "~Animal()" << endl;}

  /**
   * 让成员方法声明放在私有权限区域
   */
private:
  Animal(const Animal &other);
  Animal& operator=(const Animal &other);
};

2. C++11 delete 关键字禁止生成默认方法实现

1. 禁止对象的(左值)拷贝

[图片上传失败...(image-3f0d60-1534051110314)]

[图片上传失败...(image-3c6aa4-1534051110314)]

2. 禁止移动(右值)拷贝

[图片上传失败...(image-197b9f-1534051110314)]

3. 限制对象的内存分配区域

[图片上传失败...(image-d74c95-1534051110314)]

3. 根据C++11宏定义条件编译

#include <iostream>
using namespace std;

class Animal {
public:
  Animal(){cout << "Animal()" << endl;}
  ~Animal() {cout << "~Animal()" << endl;}

#if defined(__GXX_EXPERIMENTAL_CXX0X__) && (__cplusplus >= 201103L)
  /**
   *  C++11,直接使用delete关键字
   */
  // Animal(const Animal& other) = delete; // 声明时不用写函数参数
  Animal(const Animal&) = delete;
  Animal& operator=(const Animal&) = delete;
#else
  /**
   *  C++11以下,分为两步走:
   *  => 【声明】2个拷贝方法,禁止C++编译器自动生成默认的方法函数
   *  => 并将2个拷贝方法的声明,放在private私有区域,不让外界调用
   */
private:
  Animal(const Animal&);
  Animal& operator=(const Animal&);
#endif
};

int main()
{
  Animal a;
  // Animal b(a);
  // Animal c;
  // c = a;
}

C++11以下

->  make lan=c++
g++ main.cpp
main.cpp:27:10: error: calling a private constructor of class 'Animal'
  Animal b(a);
         ^
main.cpp:18:3: note: declared private here
  Animal(const Animal &other);
  ^
main.cpp:29:5: error: 'operator=' is a private member of 'Animal'
  c = a;
  ~ ^ ~
main.cpp:19:11: note: declared private here
  Animal& operator=(const Animal &other);
          ^
2 errors generated.
make: *** [all] Error 1
->

C++11

->  make lan=c++ ver=c++11
g++ main.cpp  -std=c++11
main.cpp:27:10: error: call to deleted constructor of 'Animal'
  Animal b(a);
         ^ ~
main.cpp:10:3: note: 'Animal' has been explicitly marked deleted here
  Animal(const Animal &other) = delete;
  ^
main.cpp:29:5: error: overload resolution selected deleted operator '='
  c = a;
  ~ ^ ~
main.cpp:11:11: note: candidate function has been explicitly deleted
  Animal& operator=(const Animal &other) = delete;
          ^
2 errors generated.
make: *** [all] Error 1
->

4. 继承boost::noncopyable类即可禁止类对象拷贝

#include <boost/utility.hpp>
#include <boost/noncopyable.hpp>

// => 缺省继承,即使用private权限继承
// => 继承 boost::noncopyable 使用 private、public 效果一样
class node : boost::noncopyable
{ 
public:
  node(){}
  ~node(){}
};

int main()
{
  node n1;
    
  // 构造拷贝
  node n2(n1); // error: call to implicitly-deleted copy constructor of 'node'
    
  // =赋值运算拷贝
  node n3;
  n3 = n1; // error: object of type 'node' cannot be assigned because its copy assignment operator is implicitly deleted
}
->  make lan=c++ ver=c++11 boost=yes
g++ main.cpp  -std=c++11 -I/usr/xiong/include/ -L/usr/xiong/lib/ -lboost_system -lstdc++
main.cpp:17:8: error: call to implicitly-deleted copy constructor of 'node'
  node n2(n1); // 构造拷贝 => error
       ^  ~~
main.cpp:6:14: note: copy constructor of 'node' is implicitly deleted because base class 'boost::noncopyable'
      (aka 'boost::noncopyable_::noncopyable') has a deleted copy constructor
class node : boost::noncopyable
             ^
/usr/xiong/include/boost/core/noncopyable.hpp:34:7: note: 'noncopyable' has been explicitly marked deleted here
      noncopyable( const noncopyable& ) = delete;
      ^
main.cpp:20:6: error: object of type 'node' cannot be assigned because its copy assignment operator is implicitly deleted
  n3 = n1; // =赋值运算拷贝 => error
     ^
main.cpp:6:14: note: copy assignment operator of 'node' is implicitly deleted because base class 'boost::noncopyable'
      (aka 'boost::noncopyable_::noncopyable') has a deleted copy assignment operator
class node : boost::noncopyable
             ^
/usr/xiong/include/boost/core/noncopyable.hpp:35:20: note: 'operator=' has been explicitly marked deleted here
      noncopyable& operator=( const noncopyable& ) = delete;
                   ^
2 errors generated.
make: *** [all] Error 1
->

5. boost::noncopyable类实现原理

#include <boost/utility.hpp>
#include <boost/noncopyable.hpp>

namespace my_noncopyable_
{
  class my_noncopyable
  {
  /**
   * 1、让构造函数和析构函数放在protected区域内,
   * => 只让该类用于代码继承,
   * => 而不能用于实例化
   */ 
  protected:
#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
    my_noncopyable() = default;
    ~my_noncopyable() = default;
#else
    my_noncopyable() {}
    ~my_noncopyable() {}
#endif
  
#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
  /**
   *  2、C++11以上
   *  => 直接让拷贝2个拷贝函数的【实现】使用delete禁用 
   */
    my_noncopyable( const my_noncopyable& ) = delete;
    my_noncopyable& operator=( const my_noncopyable& ) = delete;
#else
  /**
   *  C++11以下
   *  => 【声明】2个拷贝方法,禁止C++编译器自动生成默认的方法函数
   *  => 并将2个拷贝方法的声明,放在private私有区域,不让外界调用
   */
  private:
    my_noncopyable( const noncopyable& );
    my_noncopyable& operator=( const my_noncopyable& );
#endif
  };
}

9、static_assert() 静态断言

1. C提供的assert()只能在运行时断言

#include <stdio.h>
#include <assert.h>

int main()
{
  int a;
  scanf("%d", &a);
  assert(a == 10);
}
->  make
gcc main.c
./a.out
10
->
->  make
gcc main.c
./a.out
7
Assertion failed: (a == 10), function main, file main.c, line 8.
make: *** [all] Abort trap: 6
->

2. C++11/14 提供的static_assert()可以在编译期断言

#include <iostream>
using namespace std;

int main()
{
  static_assert(5 == sizeof(int), "4 != sizeof(int)"); // 编译期就会提示报错
}

<img src="Snip20180307_21.png" width="550" />

10、for

1. for-in(C++11)

[图片上传失败...(image-fe4f08-1534051110314)]

2. 解析为旧式的for循环遍历

[图片上传失败...(image-291a3e-1534051110314)]

11、noexcept

[图片上传失败...(image-d004eb-1534051110314)]

12、constexpr

[图片上传失败...(image-802e38-1534051110314)]

13、模板

1. 模板嵌套>符号之间不用再使用空格字符

[图片上传失败...(image-f131cf-1534051110314)]

2. 变参列表模板

[图片上传失败...(image-2382c5-1534051110314)]

3. 带有别名的模板

[图片上传失败...(image-1ad306-1534051110314)]

4. 模板参数带有默认值

#include <iostream>
#include <vector>
using namespace std;

template<typename T, typename Container = std::vector<T> >
void func(){}

int main(){}
->  make lan=c++
g++ main.cpp
main.cpp:5:31: warning: default template arguments for a function template are a C++11 extension [-Wc++11-extensions]
template<typename T, typename Container = std::vector<T> >
                              ^           ~~~~~~~~~~~~~~
1 warning generated.
./a.out
->

提示需要加上C++11标准。

5. typename 指定数据类型

template<typename T>
struct meta_func1
{
#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
  /** C++11/14 */
  using type = T*;
#else
  /** C98 */
  typedef T* type;
#endif
};

template<typename T>
struct meta_func2
{
  /**
   * 调用 meta_func1 元函数,
   * 获取其返回值type代表的数据类型,
   * 使用typename给其取一个新的别名,作为自己的类成员
   * 
   * type是当前元函数的类成员
   * => 作为当前meta_func2元函数的返回值
   * => 通过meta_func2::type进行访问
   */
#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
  using type = typename meta_func1<T>::type;
#else
  typedef typename meta_func1<T>::type type;
#endif
};

14、lambda | std::function

1. 无参数、无返回值

#include <iostream>

using namespace std;


int main(int argc, const char * argv[])
{
  auto func = []() { 
    cout << "hello world" << endl; 
  };

  func();
}

直接编译报错

->  g++ main.cpp
main.cpp:8:5: warning: 'auto' type specifier is a C++11 extension [-Wc++11-extensions]
    auto func = []() {
    ^
main.cpp:8:17: error: expected expression
    auto func = []() {
                ^
1 warning and 1 error generated.
->

lambda表达式是C++ 11支持的,g++编译时需要加上-std=c++11

->  g++ main.cpp -std=c++11
->  ./a.out
hello world
->

2. 有参数、有返回值

lambda表达式定义格式:

[capture](parameters) -> return-type {
  body
};

示例

#include <iostream>
using namespace std;

int main(int argc, const char * argv[])
{
    auto func = [](int x, int y) -> int { 
        return x + y;
    };

    cout << func(3, 4) << endl;
}
->  g++ main.cpp -std=c++11
->  ./a.out
7
->

3. lambda表达式捕获外部变量的规则

[图片上传失败...(image-d23392-1534051110314)]

1).[]不捕获任何变量。

2).[&]捕获外部作用域中所有变量,并作为引用在函数体中使用(按引用捕获)。

3).[=]捕获外部作用域中所有变量,并作为副本在函数体中使用(按值捕获)。

4).[=,&foo]按值捕获外部作用域中所有变量,并按引用捕获foo变量。

5).[bar]按值捕获bar变量,同时不捕获其他变量。

6).[this]捕获当前类中的this指针,让lambda表达式拥有和当前类成员函数同样的访问权限。如果已经使用了&或者=,就默认添加此选项。捕获this的目的是可以在lamda中使用当前类的成员函数和成员变量。

4. [=] 捕获变量的==瞬间值==

#include <iostream>
using namespace std;

int main(int argc, const char * argv[])
{
  // 1.
  int x = 1;
  int y = 2;
  
  // 2.
  auto func = [=]() {
    printf("x = %d, y = %d\n", x, y);
  };
  
  // 3. 对x,y的值进行修改
  x = 3;
  y = 4;

  // 4.
  func();
}
->  g++ main.cpp -std=c++11
->  ./a.out
x = 1, y = 2
->

类似objc中的block拷贝的只是前面出现的瞬间值,无法获取后面已经变化的值

5. [&x, y],x传引用,y传值

#include <iostream>
using namespace std;

int main(int argc, const char * argv[])
{
    //1.
    int x = 1;
    int y = 2;
    
    //2.
    auto func = [&x, y]() {
        printf("x = %d, y = %d\n", x, y);
    };
    
    //3.
    x = 3;
    y = 4;
    
    //4.
    func();
}
->  g++ main.cpp -std=c++11
->  ./a.out
x = 3, y = 2
->

由于变量x捕获的是引用(内存地址),所以能够获取变化后的值。

6. std::function

#include <iostream>
using namespace std;

int main()
{
  double a = 9.9;

  // [] 不对外部变量进行捕获,即表达式内部不能使用外部的任何变量。
  std::function<double(double)> funcP = [](double u){
    return u * 2;
  };
    
  double ret = funcP(a);
  std::cout << "ret = " << ret << std::endl;
}
->  make
g++ main.cpp -std=c++11
->  ./a.out
ret = 19.8
->

7. std::function存放到vector

#include <iostream>
#include <vector>
#include <vector>
using namespace std;

int main()
{
  std::vector<std::function<void(int,int)> > v;

  v.push_back(
    [](int a, int b){
      std::cout << "a + b = " << a+b << std::endl;
    }
  );

  v.push_back(
    [](int a, int b){
      std::cout << "a - b = " << a-b << std::endl;
    }
  );

  v.push_back(
    [](int a, int b){
      std::cout << "a * b = " << a*b << std::endl;
    }
  );

  for (auto& func : v)
    func(10, 7);
}
->  make lan=c++ ver=c++11
g++ main.cpp  -std=c++11
./a.out
a + b = 17
a - b = 3
a * b = 70
->

8. std::function 剥离类的成员方法

#include <iostream>
#include <vector>
#include <vector>
using namespace std;

class man
{
public:
  void run(){std::cout << "--- run ---" << std::endl;}
  void eat() const {std::cout << "--- eat ---" << std::endl;}
};

int main()
{
  man m;

  std::function<void(man&)> run;
  run = &man::run;
  run(m);

  std::function<void(const man&)> eat;
  eat = &man::eat;
  eat(m);
}
->  make lan=c++ ver=c++11
g++ main.cpp  -std=c++11
./a.out
--- run ---
--- eat ---
->

15、enum class

#include <iostream>
using namespace std;

enum class Color : uint8_t
// enum struct Color : uint8_t 也可以
{
  RED,
  BLUE,
  YELLOW
};

void func(Color color)
{
  switch(color)
  {
    case Color::RED:
      std::cout << "RED" << std::endl;
      break;

    case Color::BLUE:
      std::cout << "BLUE" << std::endl;
      break;

    case Color::YELLOW:
      std::cout << "YELLOW" << std::endl;
      break;
  }
}

int main(){}

16、std::bind()

可以取出struct中的成员函数单独使用

#include <iostream>
using namespace std;

struct Person {
  
  void show1(int a) {
    printf("a = %d\n", a);
  }
  
  void show2(int a, int b) {
    printf("a = %d, b = %d\n", a, b);
  }
  
  void show3(int a, int b, int c) {
    printf("a = %d, b = %d, c = %d\n", a, b, c);
  }
};

int main(int argc, const char * argv[]) 
{  
  //1.
  struct Person per;
  
  /**
   * 2. bind(要绑定的函数名, 函数来自哪个struct实例, 参数1, 参数2, ...);
   */
  auto show1 = std::bind(&Person::show1, &per, std::placeholders::_1);//一个参数
  auto show2 = std::bind(&Person::show2, &per, std::placeholders::_1, std::placeholders::_2);//二个参数
  auto show3 = std::bind(&Person::show3, &per, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);//二个参数
  
  //3.
  show1(19);
  show2(19, 20);
  show3(19, 20, 21);
}
->  g++ main.cpp -std=c++11
->  ./a.out
a = 19
a = 19, b = 20
a = 19, b = 20, c = 21
->
上一篇下一篇

猜你喜欢

热点阅读