C++ 標準模板庫的核心包括以下三個組件:
- 容器(Containers)
- deque、list、vector、map等
- 算法(Algorithms)
- 算法作用于容器。它們提供了執行各種操作的方式,包括對容器內容執行初始化、排序、搜索和轉換等操作
- 迭代器(iterators)
- 迭代器用于遍歷對象集合的元素。這些集合可能是容器,也可能是容器的子集
C++ 11 新特新
T&& 右值引用 std::move
右值引用出現之前我們只能用const引用來關聯臨時對象(右值)所以我們不能修臨時對象的內容,右值引用的出現就讓我們可以取得臨時對象的控制權,終于可以修改臨時對象了!
int main()
{
int i = 42;
int &r = i; // ok: r refers to i
int &&rr = i; // error: cannot bind an rvalue reference to an lvalue
int &r2 = i * 42; // error: i * 42 is an rvalue
const int &r3 = i * 42; // ok: we can bind a reference to const to an rvalue
int &&rr2 = i * 42;
int &&rr3 = rr2; // error: the expression rr2 is an lvalue!
return 0;
}
即凡是可以 vartype varname; 這樣定義出來的變量(variable)其自身都是左值。
std::move相關。 右值引用因為綁定對象即將被銷毀,意味著沒有人會繼續訪問他們,所以就可以把他們(的資源)steal(偷)過來。 雖然不能將右值引用綁在左值上,但通過利用utility頭文件新增的函數模板move,它返回傳入對象的右值引用,可以達到 steal的效果。
int &&rr3 = std::move(rr2); // ok
再提醒:一旦使用了move,編譯器就默認傳入對象已經不打算使用了,是可以被銷毀的,move之后該對象的值已經不確定,不要再訪問。還有由于對象偷取與復制的差別巨大,不注意會產生非常難定位的bug,所以所有使用move的地方一定要使用全稱std::move,給大家以提醒。(其實c++11在algorithm頭文件也新增了一個move,參數與意義都與此截然不同)。
#include <iostream>
using namespace std;
class HugeMem{
public:
HugeMem(int size): sz(size > 0 ? size : 1) {
c = new int[sz];
}
~HugeMem() { cout<<"HugeMem 析構\n";delete [] c; }
HugeMem(HugeMem && hm): sz(hm.sz), c(hm.c) {
cout<<"HugeMem move 構造\n";
hm.c = nullptr;
}
int * c;
int sz;
};
class Moveable{
public:
Moveable():i(new int(3)), h(1024) {}
~Moveable() { cout<<"Moveable 析構\n";delete i; }
Moveable(Moveable && m):
i(m.i), h(move(m.h)) { // 強制轉為右值,以調用移動構造函數
m.i = nullptr;
}
int* i;
HugeMem h;
};
Moveable GetTemp() {
//Moveable tmp = Moveable();
Moveable tmp;
cout << hex << "Huge Mem from " << __func__
<< " @" << tmp.h.c << endl; // Huge Mem from GetTemp @0x603030
return tmp;
}
int main() {
Moveable a(GetTemp());
cout << hex << "Huge Mem from " << __func__
<< " @" << a.h.c << endl; // Huge Mem from main @0x603030
}
早在C++11之前編譯器就把優化幾乎做到了極致——局部變量返回到函數外部并賦值給外部變量這個過程基本上不存在任何多余的臨時變量構造和析構,這比move機制更加高效。顯式指定move以后,return std::move(localvar)這里會強行從localvar移動構造一個臨時變量temp,然后return temp(temp這里會有RVO優化)。
auto for循環
需要注意的是,auto不能用來聲明函數的返回值。但如果函數有一個尾隨的返回類型時,auto是可以出現在函數聲明中返回值位置。這種情況下,auto并不是告訴編譯器去推斷返回類型,而是指引編譯器去函數的末端尋找返回值類型。在下面這個例子中,函數的返回值類型就是operator+操作符作用在T1、T2類型變量上的返回值類型。
template <typename T1, typename T2>
auto compose(T1 t1, T2 t2) -> **decltype**(t1 + t2)
{
return t1+t2;
}
auto v = compose(2, 3.14); // v's type is double
auto與for配合使用
std::map<std::string, std::vector<int>> map;
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
map["one"] = v;
for(const auto& kvp : map)
{
std::cout << kvp.first << std::endl;
for(auto v : kvp.second)
{
std::cout << v << std::endl;
}
}
int arr[] = {1,2,3,4,5};
for(int& e : arr)
{
e = e*e;
}
std::lambda
“Lambda 表達式”(lambda expression)是一個匿名函數,Lambda表達式基于數學中的λ演算得名,直接對應于其中的lambda抽象(lambda abstraction),是一個匿名函數,即沒有函數名的函數。
C++11 的 lambda 表達式規范如下:
- [ capture ] ( params ) mutable exception attribute -> ret { body } (1)
- [ capture ] ( params ) -> ret { body } (2)
- [ capture ] ( params ) { body } (3)
- [ capture ] { body } (4)
其中
(1) 是完整的 lambda 表達式形式, (2) const 類型的 lambda 表達式,該類型的表達式不能改捕獲("capture")列表中的值。 (3)省略了返回值類型的 lambda 表達式,但是該 lambda 表達式的返回類型可以按照下列規則推演出來: 如果 lambda 代碼塊中包含了 return 語句,則該 lambda 表達式的返回類型由 return 語句的返回類型確定。 如果沒有 return 語句,則類似 void f(...) 函數。 省略了參數列表,類似于無參函數 f()。
[] // 不引用外部變量 [x, &y] // x引用方式 ,y 傳值 [&] // 任何使用的外部變量都是引用方式。 [=] // 任何被使用到的外部都是傳值方式。 [&, x] // 除x傳值以外其他的都以引用方式。 [=, &z] // 除z引用以外其他的都是以傳值方式使用。
int main()
{
std::vector<int> c { 1,2,3,4,5,6,7 };
int x = 5;
c.erase(std::remove_if(c.begin(), c.end(), [x](int n) { return n < x; } ), c.end());
std::cout << "c: ";
for (auto i: c) {
std::cout << i << ' ';
}
std::cout << '\n';
// 可以用auto 接收一個lambda 表達式。
auto func1 = [](int i) { return i+4; };
std::cout << "func1: " << func1(6) << '\n';
// std::function 也可以接收lambda 表達式。
std::function<int(int)> func2 = [](int i) { return i+4; };
std::cout << "func2: " << func2(6) << '\n';
std::function<int()> func3 = [x]{return x;};
std::cout << "func3: " << func3() << '\n';
std::vector<int> someList = {1,2,3}; //這里是c++11
int total = 0;
double sum = 0.0f;
std::for_each(someList.begin(), someList.end(), [&total](int x) { total += x; });
std::cout << total << '\n';
std::for_each(someList.begin(), someList.end(), [&](int x){ total += x; sum += x;});
std::cout << total << '\n';
std::cout << sum << '\n';
//再寫一種簡單的lambda
[](){std::cout<<"就地展開的lambda\n";}();
}
bind
std::bind是STL實現函數組合概念的重要手段,std::bind綁定普通函數(函數指針)、lambda表達式、成員函數、成員變量、模板函數等
#include <iostream>
#include <functional>
void f(int n1, int n2, int n3, const int& n4, int n5)
{
std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';
}
int g(int n1)
{
return n1;
}
struct Foo {
void print_sum(int n1, int n2)
{
std::cout << n1+n2 << '\n';
}
static void static_func(std::function<int(int)> f,int n)
{
std::cout<<"call static_func\n";
std::cout<<"f(n):\t"<<f(n)<<"\n";
}
int data = 10; //c++11 支持聲明是就初始化值
};
int main()
{
using namespace std::placeholders;
// std::cref(n) 表示要把n以引用的方式傳入
int n = 7;
auto f1 = std::bind(f, _2, _1, 42, std::cref(n), n);
n = 10;
f1(1, 2, 1001); // 1 is bound by _1, 2 is bound by _2, 1001 is unused
// 綁定一個子表達式,用_3替換了 其他位置的變量
// std::bind(g, _3) 在這里已經表示int
auto f2 = std::bind(f, _4, std::bind(g, _4), _4, 4, 5);
f2(10, 11, 12 ,13);
// 綁定成員函數
Foo foo;
auto f3 = std::bind(&Foo::print_sum, foo, 95, _1);
f3(5);
// 綁定成員變量
auto f4 = std::bind(&Foo::data, _1);
std::cout << f4(foo) << '\n';
// 綁定靜態成員函數
auto f5 = std::bind(&Foo::static_func,g,_1);
f5(3);
}
std::function
通過std::function對C++中各種可調用實體(普通函數、Lambda表達式、函數指針、以及其它函數對象等)的封裝,形成一個新的可調用的std::function對象;讓我們不再糾結那么多的可調用實體。
轉換后的std::function對象的參數能轉換為可調用實體的參數; 可調用實體的返回值能轉換為std::function對象的返回值。 std::function對象最大的用處就是在實現函數回調(實際工作中就是用到了這一點),使用者需要注意,它不能被用來檢查相等或者不相等,但是可以與NULL或者nullptr進行比較。
#include <functional>
#include <iostream>
using namespace std;
std::function< int(int)> Functional;
// 普通函數
int TestFunc(int a)
{
return a;
}
// Lambda表達式
auto lambda = [](int a)->int{ return a; };
// 仿函數(functor)
class Functor
{
public:
int operator()(int a)
{
return a;
}
};
// 1.類成員函數
// 2.類靜態函數
class TestClass
{
public:
int ClassMember(int a) { return a; }
static int StaticMember(int a) { return a; }
};
int main()
{
// 普通函數
Functional = TestFunc;
int result = Functional(10);
cout << "普通函數:"<< result << endl;
// Lambda表達式
Functional = lambda;
result = Functional(20);
cout << "Lambda表達式:"<< result << endl;
// 仿函數
Functor testFunctor;
Functional = testFunctor;
result = Functional(30);
cout << "仿函數:"<< result << endl;
// 類成員函數
TestClass testObj;
Functional = std::bind(&TestClass::ClassMember, testObj, std::placeholders::_1);
result = Functional(40);
cout << "類成員函數:"<< result << endl;
// 類靜態函數
Functional = TestClass::StaticMember;
result = Functional(50);
cout << "類靜態函數:"<< result << endl;
return 0;
}
initializer_list
過往,我們這樣給vector賦值:
std::vector v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
需要感謝的是,C++11讓你更方便。
std::vector v = { 1, 2, 3, 4 };
這就是所謂的initializer list。更進一步,有一個關鍵字叫initializer list
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <typeinfo>
class MyNumber
{
public:
MyNumber(const std::initializer_list<int> &v) {
for (auto itm : v) {
mVec.push_back(itm);
}
}
void print() {
for (auto itm : mVec) {
std::cout << itm << " ";
}
}
private:
std::vector<int> mVec;
};
class Test {
public:
void show()
{
for(auto kv : nameToBirthday)
{
std::cout<<"key:\t"<<kv.first<<"\tvalue:\t"<<kv.second<<"\n";
}
}
private:
static std::map<std::string, std::string> nameToBirthday;
};
std::map<std::string,std::string> Test::nameToBirthday = {
{"lisi", "18841011"},
{"zhangsan", "18850123"},
{"wangwu", "18870908"},
{"zhaoliu", "18810316"}
};
class CompareClass
{
public:
CompareClass (int,int)
{std::cout<<"call old const\n";}
CompareClass (std::initializer_list <int> )
{std::cout<<"call initializer_list const\n";}
};
int main()
{
MyNumber m = { 1, 2, 3, 4 };
m.print(); // 1 2 3 4
Test t;
t.show();
std::map<int,int> ii_map = {{1,1},{2,2}};
CompareClass foo {10,20}; // calls initializer_list ctor
CompareClass bar (10,20); // calls first constructor
for(auto kv : ii_map)
{
std::cout<<"key:\t"<<typeid(kv.first).name()<<"\n";
}
return 0;
}
注意
本文內容轉自博客:http://blog.csdn.net/tangliguantou/article/details/50549751