C++11新特性集錦——新標準庫

新版本的標準庫添加了許多新的特性,本文只介紹特性的簡單使用,不做原理上的探究,否則篇幅將無法控制。儘管如此,這篇博客還是跳票了很久,呵呵呵

3.1智能指針std::shared_prt、std::make_shared、std::unique_ptr、std::weak_ptr

C++碼農苦new與delete久矣。使用new與delete除了經常會導致內存泄露(new之後忘了delete)之外,還經常出現delete之後繼續引用、delete之後重複delete等情況,令人防不勝防。有了智能指針日子就好過多了——前提是得正確的使用它們。錯誤的使用反而會使bug更加隱晦。

  • shared_prt是這樣一個東西:它管理一個動態的對象,並維護一個引用計數,允許多個shared_prt指向這個對象,每多一個shared_prt指向(shared_ptr之間賦值、拷貝等操作)這個對象,引用計數增1,當引用計數爲0時,銷燬該對象。
  • unique_ptr則獨立擁有一個動態對象,不允許多個智能指針指向這個對象。因此unique_ptr不支持拷貝或者賦值操作,但是支持移動操作(移動賦值與移動拷貝)。
  • weak_prt則是一個小嘍囉,它不負責管理對象的銷燬,只是簡單地指向shared_ptr所管理的對象,當一個weak_ptr綁定到一個shared_ptr時,不影響改shared_ptr的引用計數。——weak_ptr更輕量。

容易引發錯誤的使用習慣:

int * pa = new int(10);
shared_ptr<int> spb(pa); //將動態對象的管理權交給了shared_ptr
shared_ptr<int> spc(spb.get()); //禁止使用get返回的指針初始化另一個shared_ptr,spb的引用計數沒有增加,會導致異常
delete spb.get(); //禁止手動delete動態對象管理的內存。
weak_ptr<int> wpd(spb); //使用shared_ptr初始化一個weak_ptr。

......
*pa = 11; //此時動態對象有可能已被銷燬;

正確使用上述三種智能指針的示例:

auto pa = make_shared<int> (10);
auto pb = pa;
3.2 std::move、std::forward

移動語義的影子在C++11標準庫中隨處可見,他避免了很多不必要的拷貝,提升了性能。move方法能夠將一個左值在編譯期轉換爲右值。關於move與forward的用法與原理在另外兩篇博客中有討論,這裏從略。

3.3 std::function、std::bind

C++中有這樣幾種對象是可以被調用的,它們被稱爲“可調用對象

  • 普通函數
  • lambda表達式
  • 函數指針
  • 實現了operator()的對象(functor)
  • 成員函數

它們的使用方法各不相同,有了std::function之後,可以將它們以相同的形式來調用了(成員函數除外)。std::function 是一個可調用對象包裝器,是一個類模板,可以容納除了類成員函數指針之外的所有可調用對象,它可以用統一的方式處理函數、函數對象、函數指針,並允許保存和延遲它們的執行。
而std::bind則可以綁定一個可調用對象與部分參數,並將返回一個std::function 類型的結果。使用bind可以完美處理成員函數的調用——成員函數本質上就是多了一個隱含的this指針,將成員函數與對象地址邦定一下,就是一個普通的可調用對象了。

int add(int a, int b){ ...... }

class Minus{
	int operator()(int a, int b){.....}
}
Minus minusObj;
auto add2 = add; //調用add2(3, 4)等價於調用add(3, 4)。add2的類型爲std::function<int(int,int)>
auto minus2 = std::bind(Minus::operaotr(), &minusObj);//調用minus2(3, 4)等價於調用minusObj(3, 4)
//bind還可以減少參數的數量,簡化代碼
auto minus3 = std::bind(Minus::operaotr(), &minusObj, 3);//調用minus3(4)等價於調用minusObj(3, 4)

有了std::function與std::bind極大地簡化了的回調等模式的代碼,可以將繼承關係變爲聚合關係,好多繞繞的設計模式可以省下了。

3.4 std::initializer_list

在C++11之前,我們要想用一些值初始化一個vector,就得這麼幹:

vector<int> vec03;
vec03.push_back(1);
vec03.push_back(2);
。。。。
vector<int> vec11 = {1,2,3}; //c++11

int a = 3.3; //取整
int b = {3.3}//警告或報錯

其中用到的便是C++11的initializer_list,包含在標準庫頭文件中。
不僅STL中的類型可以用這個操作,普通的內置類型也推薦使用該操作,因爲類型檢查更加嚴格。另外,自定義的類,在實現了一個特殊的構造函數之後,也會支持這種形式的初始化!

class Num
{
  private:
    std::vector<int> m_vec;
  public:
    Num(const std::initializer_list<int> &v){
        for (auto a : v){
            m_vec.push_back(a);
        }
    }
}
int main{
	Num num = {4,5,6};
	return 0;
}
3.5 std::tuple 與 std::tie

tupple, 與vector array之流最大的不同在於,後者存的元素都是相同類型,前者存的元素可以是不同的類型,但大小是固定的。

auto tuple_a = make_tuple("str", 'c', 1, 1.1);
等價於
tuple<char *, char, int, double> tuple_a("str", 'c', 1, 1.1);

string a;
char b;
int c;
double d;
std::tie(a,b,c,d) = tuple_a; //通過tie把元組裏的元素解包出來
或者,通過位置get想要的值
cout<< get<0>(tuple_a)<<get<1>(tuple_a)<<endl;
獲取tuple長度:
cout<< tuple_size<decltype(tuple_a)>::value;

3.6 std::array

array與vector很像,都是數組的擴展,只不過,array是存儲在棧區的,而vector是存儲在堆區;另外array的長度必須是編譯期常量——可在編譯期間計算出來。

std::array<int, 4> arr= {1,2,3,4};
int len = 4;
std::array<int, len> arr = {1,2,3,4}; // 非法, 數組大小參數必須是常量表達式
發佈了34 篇原創文章 · 獲贊 6 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章