//myVector.h #include恩,以上代碼實現了vector的部分功能,實現了vector內存的動態分配。不過,我們可以發現以上代碼還是有幾個可以優化的地方: 1.在分配內存的時候,new將內存分配和對象構造組合在了一起。但是在vector分配內存的時候,事實上有許多內存我們可能並用不上;而如果對於這些內存進行構造對象,可能就會帶來不必要的開銷。 2.在內存重新分配的時候,我們涉及到了舊數據的轉移;不對,這裡應該說是拷貝。雖然的確應該是轉移,然而我們實現是通過拷貝。在c++11的時候提供了移動構造語義。它可以對於即將銷毀(保證重新賦值前不再使用)的對象進行移動。這樣對於某些支持移動語義的對象。移動比拷貝就可以帶來更小的開銷。 恩,要進行以上的優化我們要先介紹:allocator類,移動語義。template class myVector { public: typedef myVector _Myt; myVector() : elements(nullptr), first_free(nullptr), cap(nullptr){} // allocator成員進行默認初始化 myVector(const _Myt&); _Myt& operator=(const _Myt&); ~myVector(); T& operator[](size_t i){ return *(elements + i); } void push_back(const T&); // 添加元素 size_t size()const{ return first_free - elements; } size_t capacity()const{ return cap - elements; } T *begin()const{ return elements; } T *end()const{ return first_free; } private: void chk_n() //被添加元素函數使用 { if (size() == capacity())reallocate(); } std::pair n_copy (const T*, const T*); //被拷貝構造,賦值運算符,析構函數使用 void free(); //銷毀元素並釋放內存 void reallocate(); //獲得更多內存並拷貝已有元素 T *elements; T *first_free; T *cap; }; template myVector ::myVector(const _Myt& v) { //調用alloc_n_copy 分配空間以容納與s中一樣多的元素 auto newdata = n_copy(v.begin(), v.end()); elements = newdata.first; first_free = cap = newdata.second; } template myVector ::~myVector() { free(); } template myVector & myVector ::operator=(const _Myt& rhs) { //調用alloc_n_copy分配內存,大小與rhs一樣. auto data = n_copy(rhs.begin(), rhs.end()); free(); elements = data.first; first_free = cap = data.second; return *this; } template std::pair myVector :: n_copy(const T *b, const T *e) { auto data = new T[e - b]; for (auto i = b; i < e; i++) data[i-b] = *i; return{ data, data + (e - b) }; } template void myVector ::push_back(const T& s) { chk_n(); //確保已有新空間 *(first_free++) = s; } template void myVector ::free() { delete[] elements; } template void myVector ::reallocate() { //我們將分配當前大小兩倍的內存空間 auto newcapacity = size() ? 2 * size() : 1; //分配新內存 auto newdata = new T[newcapacity]; auto dest = newdata; auto elem = elements; //將數據從舊地址移動到新地址 for (size_t i = 0; i != size(); ++i) *(dest++) = *(elem++); free(); //一旦更新完就要釋放舊內存 elements = newdata; first_free = dest; cap = elements + newcapacity; }
int main() { allocator不僅這樣,標准庫還提供了一些算法讓我們使用的時候更加方便: |函數|介紹| |—|—-| |uninitialized_copy(b,e,b2)|從迭代器b和e指出的輸入范圍中拷貝元素到迭代器b2指定的未構造的原始內存中。b2指向的內存必須足夠大,能容納輸入序列中元素的拷貝。 |uninitialized_copy_n(b,n,b2)|從迭代器b指向的元素開始,拷貝n個元素到b2開始的內存中。 |uninitialized_fill(b,e,t)|在迭代器b,e指定的原始內存范圍中創建對象,對象的值均為t的拷貝。 |uninitialized_fill_n(b,n,t)|從迭代器b指向的內存地址開始創建n個對象。b必須指向足夠大的未構造原始內存,能夠容納給定數量的對象。 值得注意的是,所有通過allocate分配的內存都必須通過deallocate去回收,而所有構造的對象都必須通過destroy去釋放。所以這些拷貝算法都必須要求原始內存,如果內存上有對象,請先使用destroy釋放!!alloc; //這個對象可以用來分配 string 類型的內存。 string* p = alloc.allocate(5); //使用alloc對象分配5個string對象大的連續內存並將頭指針給p。 //allocate分配的內存在沒有構造之前是不能使用的!! alloc.construct(p,"hello world"); //使用"hello world"構造string cout << *p << endl; //輸出剛才構造的string,輸出hello world alloc.destroy(p); //銷毀剛才構造的對象 alloc.deallocate(p,5); //釋放內存 return 0; }
int i = 42; int &r = i; //正確:r引用i int &&rr = i; //錯誤:不能將一個右值引用綁定到左值上 int &r2 = i * 42; //錯誤:i * 42 是個右值 const int & r3 = i * 432; //正確:我們可以把一個const引用綁定到右值上 int &&rr2 = i * 42; //正確:右值引用綁定右值考察左值和右值的區別:左值有持久的狀態,而右值要麼是字面常量,要麼是在表達式求值過程中或者是函數返回的時候創建的臨時變量。
template值得注意的是,我們要保證移後源對象必須是可析構狀態,而且如果移動構造(賦值)函數不拋出異常的話必須要標記為noexcept(primer p474)。 對於移動賦值運算符我們要保證能正確處理自我賦值:myVector ::myVector(_Myt&& v):elements(v.elements),first_free(v.first_free),cap(v.cap){ v.elements = v.first_free = v.cap = nullptr; }
template當然,和其他構造函數一樣,如果我們沒有定義移動構造函數的時候,編譯器會給我們提供默認的移動構造函數。不過,前提是該類沒有定義任何版本的拷貝控制函數以及每個非staitc成員變量都可以移動。編譯器就會默認為它合成移動構造函數和移動賦值運算符。myVector & myVector ::operator=(_Myt&& rhs) { if (this != &rhs) { free(); elements = rhs.elements; first_free = rhs.first_free; cap = rhs.cap; //將rhs置為可析構狀態 rhs.elements = rhs.first_free = rhs.cap = nullptr; } }
#pragma once #include盡管,以上的代碼vector只實現了vector很少的一部分功能,而且可能實現方式也有不足的地方。不過,在這裡只是想體現動態內存的使用。所以,以上的代碼還是可以作為c++動態內存管理的的示例的。 基本上c++動態內存管理的就介紹到這裡了。template class myVector { public: typedef myVector _Myt; myVector() : elements(nullptr), first_free(nullptr), cap(nullptr){} // allocator成員進行默認初始化 myVector(const _Myt&); myVector(_Myt&&); _Myt& operator=(const _Myt&); _Myt& operator=(_Myt&&); ~myVector(); T& operator[](size_t i){ return *(elements + i); } void push_back(const T&); // 添加元素 size_t size()const{ return first_free - elements; } size_t capacity()const{ return cap - elements; } T *begin()const{ return elements; } T *end()const{ return first_free; } private: static std::allocator alloc; void chk_n_alloc() //被添加元素函數使用 { if (size() == capacity())reallocate(); } std::pair alloc_n_copy (const T*, const T*); //被拷貝構造,賦值運算符,析構函數使用 void free(); //銷毀元素並釋放內存 void reallocate(); //獲得更多內存並拷貝已有元素 T *elements; T *first_free; T *cap; }; template std::allocator myVector ::alloc; template myVector ::myVector(const _Myt& v) { //調用alloc_n_copy 分配空間以容納與s中一樣多的元素 auto newdata = alloc_n_copy(v.begin(), v.end()); elements = newdata.first; first_free = cap = newdata.second; } template myVector ::myVector(_Myt&& v):elements(v.elements),first_free(v.first_free),cap(v.cap){ v.elements = v.first_free = v.cap = nullptr; } template myVector ::~myVector() { free(); } template myVector & myVector ::operator=(const _Myt& rhs) { //調用alloc_n_copy分配內存,大小與rhs一樣. auto data = alloc_n_copy(rhs.begin(), rhs.end()); free(); elements = data.first; first_free = cap = data.second; return *this; } template myVector & myVector ::operator=(_Myt&& rhs) { if (this != &rhs) { free(); elements = rhs.elements; first_free = rhs.first_free; cap = rhs.cap; //將rhs置為可析構狀態 rhs.elements = rhs.first_free = rhs.cap = nullptr; } } template std::pair myVector :: alloc_n_copy(const T *b, const T *e) { auto data = alloc.allocate(e - b); //初始化並返回一個pair,該pair由data和uninitialized_copy組成 return{ data, uninitialized_copy(b, e, data) }; } template void myVector ::push_back(const T& s) { chk_n_alloc(); //確保已有新空間 alloc.construct(first_free++, s); } template void myVector ::free() { //不能傳遞給deallocate一個空指針,如果elements為NULL,那麼函數什麼都不做 if (elements) { //逆序銷毀所有元素 for (auto p = first_free; p != elements;/* 空 */) alloc.destroy(--p); alloc.deallocate(elements, cap - elements); } } template void myVector ::reallocate() { //我們將分配當前大小兩倍的內存空間 auto newcapacity = size() ? 2 * size() : 1; //分配新內存 auto newdata = alloc.allocate(newcapacity); //將數據從舊地址移動到新地址 auto dest = newdata; auto elem = elements; for (size_t i = 0; i != size(); ++i) alloc.construct(dest++, std::move(*elem++)); free(); //一旦更新完就要釋放舊內存 elements = newdata; first_free = dest; cap = elements + newcapacity; }