本身一直用的是C++98规范来编程,对于C++11只闻其名却没用过其特性。近期由于工做的须要,须要掌握C++11的一些特性,因此查阅了一些C++11资料。由于本身有C++98的基础,因此从C++98过渡到C++11并不算特别吃力,读了一些书籍后,算是对C++11有了个比较基础的理解,感受程序员仍是要常常保持新语言新特性的更新,如今 C++ 标准都出到C++17了!这篇文章就是对C++11一些经常使用新特性的一些总结,以C++98和 C++11在语法上的差别来突出C++11新特性的非凡优点。ios
auto的自动推导,用于从初始化表达式中推断出变量的数据类型。c++
//C++98 int a = 10; string s = "abc"; float b = 10.0; vector<int> c; vector<vector<int> > d; map<int, string> m; m[1] = "aaa"; map<int, string>::iterator it = m.begin(); //C++11 auto a1 = 10; //a1为int auto s1 = "abc"; //s1为string auto b1 = b; auto c1 = c; auto d1 = d; auto e1 = 'a'; int* x = &a1; auto d1 = x; auto m1 = m.begin(); auto x=1,y=2; //ok auto i=1.j=3.14; //compile error double a2 = 3.144; const auto a3 = a2; //const double auto a4 = a2; //double volatile int c2 = 3; auto c3 = c2; //int
decltype能够经过一个变量或表达式获得类型。程序员
#include <iostream> #include <vector> using namespace std; int add(int a) { return ++a; } void fun(int a) { cout << "call function: [int]\n" << endl; } void fun(int *a) { cout << "call function: [int*]\n" << endl; } int main() { //C++11 int aa = 10; decltype(aa) bb = 11; string ss = "hello intel"; decltype(ss) ss1 = "hello"; const vector<int> vec(1); decltype(vec[0]) cc = 1; decltype(0) dd = vec[0]; //dd是int类型 decltype(add(1)) ee; //int int a[5]; decltype(a) ff; //int[5] //decltype(fun) gg; 没法经过编译,是个重载函数 return 0; }
空指针标识符nullptr是一个表示空指针的标识,他不是一个整数,这是与咱们经常使用的NULL宏的区别。NULL只是一个定义为常整数0的宏,而nullptr是C++11的一个关键字,一个內建的标识符。算法
#include <iostream> #include <vector> using namespace std; void fun(int a) { cout << "call function: [int]\n" << endl; } void fun(int *a) { cout << "call function: [int*]\n" << endl; } int main() { //C++11 fun(NULL); //call function: [int] fun(nullptr); //call function: [int*] int* p = NULL; fun(p); //call function: [int*] return 0; }
C++98和C++11在使用语法上的差别以下:编程
#include <iostream> #include <vector> using namespace std; int main() { //C++98 vector<int> vec(8, 1); cout << "C++98 range for:" << endl; for (vector<int>::iterator it = vec.begin(); it != vec.end(); it++) { cout << *it << endl; } //C++11 cout << "C++11 range for:" << endl; for (auto d : vec) { cout << d << endl; } return 0; }
值得指出的是,是否可以使用基于范围的for循环,必须依赖一些条件。首先,就是for循环迭代的范围是可肯定的。对于类来讲,若是该类有begin和end函数,那么for_each之间就是for循环迭代的范围。对于数组而言,就是数组的第一个和最后一个元素间的范围。其次,基于范围的for循环还要求迭代的对象实现+ + 和==等操做符。对于STL中的容器,如string、array、map等使用起来是不会有问题的。下面是C++11操做vector和数组的实践:数组
#include <iostream> #include <vector> using namespace std; int main() { vector<int> vec(8, 1); //C++11 cout << "C++11 value range for:" << endl; /*d非引用,修改d不会影响vector里的值*/ for (auto d : vec) //d中存储的是vec中的值 { d = 2; } for (auto d : vec) { cout << d << endl; } cout << "C++11 reference range for:" << endl; /*当迭代变量d为引用时,vector里的值能够被修改*/ for (auto &d : vec) { d = 2; } for (auto d : vec) { cout << d << endl; } //数组for_each char arr[] = {'a','b','c','d'}; for (auto &d : arr) { d -= 32; } for (auto d : arr) { cout << d << endl; } //遍历二维数组,注意迭代变量row必须为引用。若是你想用 range for 的方法,来遍历更高维的数组 (dim > 2),那么你只须要:除了最内层循环以外,其余全部外层循环都加入 '&' 便可。 int array2[5][5] = {0}; for (auto &row : array2) for (auto col : row) cout << col << endl; return 0; }
先看下面这个例子,编译器在推导decltype(t1+t2)时表达式中t1和t2都未声明,因此编译失败。安全
#include <iostream> #include <vector> using namespace std; template<class T1, class T2> decltype(t1 + t2) sum(T1 t1, T2 t2) { return t1 + t2; } int main() { auto total = sum(1, 2); cout << total << endl; return 0; }
因此C++11引入新语法,即把函数的返回值移至参数声明以后,复合符号->decltype(t1+t2)被称为追踪返回类型。而本来的函数返回值由auto占据。ide
#include <iostream> #include <vector> using namespace std; template<class T1, class T2> auto sum(T1 t1, T2 t2) ->decltype(t1+t2) { return t1 + t2; } int main() { auto total = sum(1, 2); cout << total << endl; return 0; }
struct B { virtual void f1(int) const; virtual void f2(); void f3(); }; struct D1 : public B { void f1(int) const override; //ok void f2(int) override; //error,B中没有形如f2(int)的函数 void f3() override; //error,f3不是虚函数 void f4() override; //error,B中无f4函数 }; struct D2 : public B { void f1(int) const final; //不准后续的其余类覆盖 }; struct D3 :public D2 { void f2(); void f1(int) const; //error,final函数不可覆盖 };
final还能够用于防止继承的发生函数
class NoDerived final { }; class Bad :NoDerived //NoDerived不可作基类 { }; class Base { }; class Last final :Base { }; class Bad2 :Last //Last不可作基类 { };
对于 C++ 的类,若是程序员没有为其定义特殊成员函数,那么在须要用到某个特殊成员函数的时候,编译器会隐式的自动生成一个默认的特殊成员函数,好比拷贝构造函数,或者拷贝赋值操做符。性能
C++11容许咱们使用=default来要求编译器生成一个默认构造函数,也容许咱们使用=delete来告诉编译器不要为咱们生成某个默认函数
class B { B() = default; //显示声明使用默认构造函数 B(const B&) = delete; //禁止使用类对象之间的拷贝 ~B() = default; //显示声明使用默认析构函数 B& operator=(const B&) = delete; //禁止使用类对象之间的赋值 B(int a); };
简单来讲,Lambda函数也就是一个函数(匿名函数),它的语法定义以下:
[capture](parameters) mutable ->return-type{statement}
#include <iostream> using namespace std; int main() { auto f = []() {cout << "hello world!" << endl; }; f(); //hello world! int a = 123; auto f1 = [a] { cout << a << endl; }; f1(); //123 auto f2 = [&a] {cout << a << endl; }; a = 789; f2(); //789 //隐式捕获:让编译器根据函数体中的代码来推断须要捕获哪些变量 auto f3 = [=] {cout << a << endl; }; f3(); //789 auto f4 = [&] {cout << a << endl; }; a = 990; f4(); //990 auto f5 = [](int a, int b)->int {return a + b; }; printf("%d\n", f5(1, 2)); //3 return 0; }
lambda表达式在C++下的应用,排序
#include <stdio.h> #include <algorithm> #include <vector> using namespace std; void print(char arr[], int len) { for (int i = 0; i < len; i++) { printf("%d ", arr[i]); } printf("\n"); } bool cmp(char a, char b) { if (a > b) return true; else return false; } int main() { //c++98 char arr1[] = { 2,5,2,1,5,89,36,22,89 }; int len = sizeof(arr1) / sizeof(char); sort(arr1, arr1 + len, cmp); print(arr1, len); //c++11 char arr2[] = { 2,5,2,1,5,89,36,22,89 }; int len2 = sizeof(arr2) / sizeof(char); sort(arr2, arr2 + len2, [](char a, char b)->bool {return a > b; }); print(arr2, len2); return 0; }
std::move是为性能而生,经过std::move,能够避免没必要要的拷贝操做。std::move是将对象的状态或者全部权从一个对象转移到另外一个对象,只是转移,没有内存的搬迁或者内存拷贝。
#include <iostream> #include <utility> #include <vector> #include <string> int main() { std::string str = "Hello"; std::vector<std::string> v; //调用常规的拷贝构造函数,新建字符数组,拷贝数据 v.push_back(str); std::cout << "After copy, str is \"" << str << "\"\n"; //After move, str is "Hello" //调用移动构造函数,掏空str,掏空后,最好不要使用str v.push_back(std::move(str)); std::cout << "After move, str is \"" << str << "\"\n"; //After move, str is "" std::cout << "The contents of the vector are \"" << v[0] << "\", \"" << v[1] << "\"\n"; //The contents of the vector are "Hello", "Hello" }
使用 std::array保存在栈内存中,相比堆内存中的 std::vector,咱们就可以灵活的访问这里面的元素,从而得到更高的性能;同时正式因为其堆内存存储的特性,有些时候咱们还须要本身负责释放这些资源。
使用std::array可以让代码变得更加现代,且封装了一些操做函数,同时还可以友好的使用标准库中的容器算法等等,好比 std::sort。
std::array 会在编译时建立一个固定大小的数组,std::array 不可以被隐式的转换成指针,使用 std::array 很简单,只需指定其类型和大小便可:
#include <stdio.h> #include <algorithm> #include <array> void foo(int* p) { } int main() { std::array<int, 4> arr = {4,3,1,2}; foo(&arr[0]); //OK foo(arr.data()); //OK //foo(arr); //wrong std::sort(arr.begin(), arr.end()); //排序 return 0; }
std::forward_list 使用单向链表进行实现,提供了 O(1) 复杂度的元素插入,不支持快速随机访问(这也是链表的特色),也是标准库容器中惟一一个不提供 size() 方法的容器。当不须要双向迭代时,具备比 std::list 更高的空间利用率。
#include <stdio.h> #include <algorithm> #include <iostream> #include <string> #include <forward_list> int main() { std::forward_list<int> list1 = { 1, 2, 3, 4 }; //从前面向foo1容器中添加数据,注意不支持push_back list1.pop_front(); //删除链表第一个元素 list1.remove(3); //删除链表值为3的节点 list1.push_front(2); list1.push_front(1); list1.push_front(14); list1.push_front(17); list1.sort(); for (auto &n : list1) { if (n == 17) n = 19; } for (const auto &n : list1) { std::cout << n << std::endl; //1 2 2 4 14 19 } return 0; }
无序容器中的元素是不进行排序的,内部经过 Hash 表实现,插入和搜索元素的平均复杂度为 O(constant),在不关心容器内部元素顺序时,可以得到显著的性能提高。
C++11 引入了两组无序容器:std::unordered_map/std::unordered_multimap 和 std::unordered_set/std::unordered_multiset。
下面给出unordered_map和unordered_set的使用方法。
#include <stdio.h> #include <algorithm> #include <iostream> #include <string> #include <unordered_map> #include <unordered_set> void foo(int* p) { } int main() { //unordered_map usage std::unordered_map<std::string, int> um = { {"2",2},{"1",1},{"3",3} }; //遍历 for (const auto &n : um) { std::cout << "key:" << n.first << " value:" << n.second << std::endl; } std::cout << "value:" << um["1"] << std::endl; //unordered_set usage std::unordered_set<int> us = { 2,3,4,1}; //遍历 for (const auto &n : us) { std::cout << "value:" << n << std::endl; } std::cout << "value:" << us.count(9) << std::endl; //判断一个数是否在集合内,1存在0不存在 std::cout << "value:" << *us.find(1) << std::endl; //查找一个特定的数是否在集合内,找到就返回该数的迭代器位置 return 0; }
shared_ptr使用引用计数,每个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,删除所指向的堆内存。shared_ptr内部的引用计数是安全的,可是对象的读取须要加锁。
#include <stdio.h> #include <memory> #include <iostream> int main() { //auto ptr = std::make_shared<int>(10); std::shared_ptr<int> ptr(new int(10)); std::shared_ptr<int> ptrC(ptr); auto ptr2 = ptr; { auto ptr3 = ptr2; std::cout << "pointer1.use_count() = " << ptr.use_count() << std::endl; //4 std::cout << "pointer2.use_count() = " << ptr2.use_count() << std::endl; //4 } std::cout << "pointer1.use_count() = " << ptr.use_count() << std::endl; //3 std::cout << "pointer2.use_count() = " << ptr2.use_count() << std::endl; //3 int *p = ptr.get(); //获取原始指针 std::cout << "pointer1.use_count() = " << ptr.use_count() << std::endl; //3 std::cout << "pointer2.use_count() = " << ptr2.use_count() << std::endl; //3 return 0; }
std::unique_ptr 是一种独占的智能指针,它禁止其余智能指针与其共享同一个对象,从而保证代码的安全:
#include <stdio.h> #include <memory> #include <iostream> int main() { std::unique_ptr<int> ptr(new int(10)); //auto ptr2 = ptr; //非法 //虽然说unique_ptr是不可复制的,但咱们可使用std::move将其独占权转移到其余的unique_ptr auto ptr2(std::move(ptr)); std::cout << *ptr2 << std::endl; return 0; }
先观察下面的代码,若是咱们在类father中使用的是shared_ptr
father ! son !
以上问题就是shared_ptr的环形引用问题。为了不shared_ptr的环形引用问题,须要引入一个弱引用weak_ptr, weak_ptr是为了配合shared_ptr而引入的一种智能指针,弱引用不会引发引用计数增长,它更像是shared_ptr的一个助手而不是智能指针,由于它不具备普通指针的行为,没有重载operator*和->,它的最大做用在于协助shared_ptr工做,像旁观者那样观测资源的使用状况.
#include <iostream> #include <memory> using namespace std; class father; class son; class father { public: father() { cout << "father !" << endl; } ~father() { cout << "~~~~~father !" << endl; } void setSon(shared_ptr<son> s) { son = s; } private: //shared_ptr<son> son; weak_ptr<son> son; // 用weak_ptr来替换 }; class son { public: son() { cout << "son !" << endl; } ~son() { cout << "~~~~~~son !" << endl; } void setFather(shared_ptr<father> f) { father = f; } private: shared_ptr<father> father; }; void test() { shared_ptr<father> f(new father()); shared_ptr<son> s(new son()); f->setSon(s); s->setFather(f); } int main() { test(); return 0; }
输出:
father ! son ! ~~~~~~son ! ~~~~~father !