18.1 優化內存分配
C++的內存分配是一種類型化操作:new為特定類型分配內存,並在新分配的內存中構造該類型的一個對象。new表達式自動運行合適的構造函數來初始化每個動態分配的類類型。
18.1.1 C++中的內存分配
C++中,內存分配和對象構造緊密糾纏,就像析構和內存回收一樣。使用new表達式的時候,分配內存,並在該內存中構造一個對象;使用delete表達式時,調用析構函數撤銷對象,並將對象所用內存返還給系統。
接管內存分配時,必須處理兩個任務。分配原始內存時,必須在該內存中構造對象;在釋放該內存之前,必須保證適當地撤銷這些對象。
對未構造的內存中的對象進行賦值而不是初始化,其行為是未定義的。對許多類而言,這樣做引起運行時崩潰。賦值涉及刪除現存對象,如果沒有現存對象,賦值操作符中的動作就會有災難性效果。
C++提供下面兩種方法分配和釋放未構造的原始內存:
(1) allocater類,它提供可感知類型的內存分配。這個類支持一個抽象接口,以分配內存並隨後使用該內存保護對象。
(2)標准庫中的operator new和operator delete,它們分配和釋放需要大小的原始的、未類型化的內存。
C++還提供不同的方法在原始內存中構造和撤銷對象。
(1)allocator類定義了名為construct和destroy的成員,其操作正如它們的名字所指出的那樣:construct成員在未構造內存中初始化對象,destroy成員在對象上運行適當的析構函數。
(2)定位new表達式(placement new expression)接受指向未構造內存的指針,並在該空間中初始化一個對象或一個數組。
(3)可以直接調用對象的析構函數來撤銷對象。運行析構函數並不釋放對象所在的內存。
(4)算法uninittialized_fill和uninitialized_copy像fill和copy算法一樣執行,除了它們在目的地構造對象而不是給對象賦值之外。
現代C++程序一般應該使用allocator類來分配內存,它更安全靈活。但是,在構造對象的時候,用new表達式比allocator::construct成員更靈活。有幾種情況下必須用new。
18.1.2 allocator類
allocator類是一個模板,它提供類型化的內存分配以及對象構造和撤銷。
allocator類將內存分配和對象構造分開。當allocator對象分配內存的時候,它分配適當大小並排列成保存給定類型的對象的空間。但是,它分配的內存是未構造的,allocator的用戶必須分別construct和destroy放置在該內存中的對象。
1. 使用allocator管理類成員數據
vector所用存儲開始是未構造內存,它還沒有保存任何對象。將元素賦值或增加到這個預分配空間的時候,必須使用allocator類的construct成員構造元素。
template<class T>
class Vector{
public:
Vector():elements(0),first_free(0),end(0){}
void push_back(const T&);
private:
static std::allocator<T> alloc;
void reallocate();
T* elements; //first element
T* first_free; //behind the last actual element
T* end; //behind vector conent
};
template<class T>
class Vector{
public:
Vector():elements(0),first_free(0),end(0){}
void push_back(const T&);
private:
static std::allocator<T> alloc;
void reallocate();
T* elements; //first element
T* first_free; //behind the last actual element
T* end; //behind vector conent
};
2. 使用construct
template<class T>
void Vector<T>::push_back(const T& t){
if(first_free==end)
reallocate(); //gets more space and copies existing elements to it
alloc.construct(first_free,t);
++first_free;
}
template<class T>
void Vector<T>::push_back(const T& t){
if(first_free==end)
reallocate(); //gets more space and copies existing elements to it
alloc.construct(first_free,t);
++first_free;
}3. 重新分配元素與復制元素
template <class T>
void Vector<T>::reallocate(){
std::ptrdiff_t size=first_free-elements;
std::ptrdiff_t newcapacity=2*max(size,1);
T* newelements=alloc.allocate(newcapacity);
uninitialized_copy(elements,first_free,newelements);
for(T *p=first_free;p!=elements;){
alloc.destroy(--p);
}
if(elements)
alloc.deallocate(elements,end-elements);
elements=newelements;
first_free=elements+size;
end=elements=newcapacity;
}
template <class T>
void Vector<T>::reallocate(){
std::ptrdiff_t size=first_free-elements;
std::ptrdiff_t newcapacity=2*max(size,1);
T* newelements=alloc.allocate(newcapacity);
uninitialized_copy(elements,first_free,newelements);
for(T *p=first_free;p!=elements;){
alloc.destroy(--p);
}
if(elements)
alloc.deallocate(elements,end-elements);
elements=newelements;
first_free=elements+size;
end=elements=newcapacity;
}
每次重新分配時分配兩倍內存。
deallocate期待指向由allocate分配的空間的指針,傳給deallocate一個零指針是不合法的。
摘自 xufei96的專欄