在之前寫了一篇隨筆,但是查了資料后,感覺理解的有問題,所以從新總結下,原文在分割線下。
C++中運算符new的使用,我們在教科書中學到的就是創建一個對象並初始化。其實他可以分成兩個步驟:
- 配置內存
- 初始化
Point3d *origin = new Point3d;
會被c++編譯器解析成如下偽碼:
Point3d * origin;
if(origin = __new (sizeof(Point3d)))
origin = Point3d::Point3d(origin);
也就是解析成兩步,第一步是采用new 運算符來分配內存,第二部是調用構造函數來初始化對象。
在VC的代碼中,<new>中包括了兩個new的函數,可以看下面的文章,第一個函數就是分配內存的,通過源碼可以看到他調用的是malloc函數來完成的。第二個函數是一個稱為 placement operator new的函數,它有兩個參數。要調用它需要通過如下方式:
Point2w *ptr = new (arena) Point2w;
其中arena是已經分配好的內存了。
也就是說編譯器已經支持了對指定內存進行初始化的方法,而stl中就是利用的這個函數,來實現對對象的construct。
------------------------------------------------華麗的分割線-------------------------------------------------------
通過看STL的源碼,確實學到了很多C++基礎的東西,在看<new>這個頭文件時,看到了new函數的原型如下:
__bcount(_Size) void *__CRTDECL operator new(size_t _Size) _THROW1(std::bad_alloc);
inline void *__CRTDECL operator new(size_t, void *_Where) _THROW0()
{ // construct array with placement at _Where
return (_Where);
}
在前幾天寫的一篇隨筆中,寫了C++在構造一個對象時,采用new函數來分配內存,分配好的內存是由構造函數來進行初始化的。 在看<xmemory>中的源碼時,看到如下代碼
// TEMPLATE FUNCTION _Allocate
template<class _Ty> inline
_Ty _FARQ *_Allocate(_SIZT _Count, _Ty _FARQ *)
{ // check for integer overflow
.........//省略掉一些代碼
// allocate storage for _Count elements of type _Ty
return ((_Ty _FARQ *)::operator new(_Count * sizeof (_Ty)));
}
// TEMPLATE FUNCTION _Construct
template<class _T1, class _T2> inline
void _Construct(_T1 _FARQ *_Ptr, const _T2& _Val)
{ // construct object at _Ptr with value _Val
void _FARQ *_Vptr = _Ptr;
::new (_Vptr) _T1(_Val);
}
按照我的理解,_Construct中應該是調用構造函數的地方,_Allocate中純粹的是分配內存。按照這個步驟,是不是我們所學的C++中的所有對象都可以采用類似的步驟呢?
從教課書上學到的知識來看,我們定義一個類 ABC后,采用 new ABC;就可以返回一個已經經過構造函數初始化的對象的地址,沒有告訴我們該如何去分兩步來進行。
從<xmemory>代碼中我們可以學習到如何將分配內存和初始化分開進行,這兩步主要映射到第一段代碼中的兩個new函數。C++編譯器在看到new 一個對象的調用時,會調用new函數來分配內存,然后調用構造函數來初始化。構造函數的調用是有編譯器自動在new函數調用之后調用的。如果要想分成兩步,就是想辦法把構造函數的調用單獨抽出來,但是以前沒見過怎么單獨調用構造函數。
第一段代碼中的第一個new函數中,可以達到分配內存的目的,然后系統調用構造函數。而當第二個operator new(size_t, void *_Where)時,沒有內存分配,直接返回源地址,然后系統調用構造函數,就可以達到只調用構造函數的目的。
自己寫代碼試了一下:
CA::CA(int a):num(a)
{
std::cout<<"ca construct"<<std::endl;
std::cout<<num<<std::endl;
}
CA::~CA(void)
{
std::cout<<"ca destruct"<<std::endl;
}
int main()
{
CA* ca = (CA*)::operator new(sizeof(CA));
::new (ca) CA(2);
delete ca;
CA *a = new CA(3);
delete(a);
}
這樣的寫法是沒有見過的,但是確實可以達到我們上面的將內存分配和構造函數分開調用的目的。