C++為我們提供了安全的內存空間申請方式與釋放方式,但是new與delete表達式卻是把空間的分配回收與對象的構建銷毀緊緊的關聯在一起。實際上,作為與C語言兼容的語言,C++也為我們提供了更加底層的內存操作方式的。
談C++就離不開STL,考慮一下vector<>類的機制,為了高效率的增加與刪除元素,它並不會在我們每次進行添加或刪除操作時進行內存的分配與回收,而是會提前預留下一片空間。我們通過size函數可以得到容器內元素的個數,通過capacity函數則可以得到該容器的實際大小。實際上每個容器都有自己的Allocator類,用於進行空間的分配與回收,對象的構造與銷毀。下面的代碼來自與《C++ primer》,是vector類的push_back函數的一種可能實現方式:
templatevoid Vector ::push_back(const T& t) { // are we out of space? if (first_free == end) reallocate(); // gets more space and copies existing elements to it alloc.construct(first_free, t); ++first_free; }
first_free指向容器中第一個空閒的塊,如果已經沒有空閒塊了,則通過reallocate函數重新分配。alloc是Alloctor
當我們使用new表達式,來調用拷貝構造函數時實際上時伴隨著空間的分配的。那麼Allocator是怎麼做到的呢?實際上它是調用了C++的一個內置的操作符:
void *operator new(size_t); // allocate an object void *operator new[](size_t); // allocate an array new (place_address) type new (place_address) type (initializer-list)
這寫重載操作符函數可以進行內存的分配以及在指定的內存空間進行對象的構造。需要注意的是它們並非new表達式,new表達式是不可以被重載的,實際上new表達式底層也就是調用了這些重載函數的。前兩個用內存的分配,後兩個則用於對象的構造。Alloctor
new (first_free) T(const T& t);
我們再來看一下reallocate函數的實現:
templatevoid Vector ::reallocate() { // compute size of current array and allocate space for twice as many elements std::ptrdiff_t size = first_free - elements; std::ptrdiff_t newcapacity = 2 * max(size, 1); // allocate space to hold newcapacity number of elements of type T T* newelements = alloc.allocate(newcapacity); // construct copies of the existing elements in the new space uninitialized_copy(elements, first_free, newelements); // destroy the old elements in reverse order for (T *p = first_free; p != elements; /* empty */ ) alloc.destroy(--p); // deallocate cannot be called on a 0 pointer if (elements) // return the memory that held the elements alloc.deallocate(elements, end - elements); // make our data structure point to the new elements elements = newelements; first_free = elements + size; end = elements + newcapacity; }
這個實現就稍微復雜一點了,邏輯上我就不說了,著重說明一下其中幾個函數的使用吧。首先是Alloctor
return operator new[](newcapacity * sizeof(T));
uninitialized_copy函數實際上是memory頭文件中的一個函數,它的聲明形式如下:
templateForwardIterator uninitialized_copy ( InputIterator first, InputIterator last, ForwardIterator result );
elements指針指向的是vector內部維護的線性表的首地址,該函數的調用實際上將elements與first_free所限定的區域裡的對象拷貝到由newelements 所指向的新分配的的空間中去,其底層也是使用的是拷貝構造函數。
然後是關於Alloctor
接下來是關於Alloctor
void *operator delete(void*); // free an object void *operator delete[](void*); // free an array
這兩個版本是分別用來釋放單個對象以及數組對象的。