C++中在指定的内存位置,调用构造函数
在之前写了一篇随笔,但是查了资料后,感觉理解的有问题,所以从新总结下,原文在分割线下。
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);
}
这样的写法是没有见过的,但是确实可以达到我们上面的将内存分配和构造函数分开调用的目的。