C++11 新特性
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 总结
- 1、C++11之后的标准下,应该尽量多的使用auto
- 2、auto不会有任何的效率损失,都是基于编译期的推导
- 3、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以下禁止生成默认方法实现
- 手动写上 方法声明,不让编译器生成函数实现
- 并将方法声明放在 private 区域不让外界调用
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
->