小探new
c++中的new表达式说简单也简单,就是动态创建对象嘛。说不简单也不简单,因为类的操作符new是可以重载的,而且c++中还存在这样的写法int * p = new (p_other) int。这。。。
经过翻阅c++ primer和一通百度谷歌,跑到各种大神博客学习,终于有点懂了。
一、搞懂
其实new表达式有三种形式。
1.动态分配对象
c++ primer说的好:
int *pi = new int( 0 );
new 表达式的操作序列如下:从空闲存储区分配对象,然后用括号内的值初始化该对象。为从空闲存储区分配对象,new 表达式调用库操作符 new()。前面的 new 表达式与下列代码,序列大体上等价:
int ival = 0; // 创建一个用 0 初始化的 int 对象 ,我的理解:这句代码其实是由调用库操作符new()完成的,且该操作符位于全局命名空间。
int *pi = &ival; // 现在指针指向这个对象
当然,不同的是 pi 指向的对象是由库操作符 new()分配的,位于程序的自由存储区中。类似地,如下语句:
iStack *ps = new iStack( 512 );
创建了一个内含 512 个元素的 iStack 型的对象,在类对象的情况下,括号中的值被传递给该类相关的构造函数,它在该对象被成功分配之后才被调用。
网上大神写的好,即序列大体上等价于伪码:
iStack *ps = (iStack*)malloc(sizeof(iStack)); //我的理解:这句代码其实是由调用类的操作符new()完成的,如果重载了就调用重载的,如果没有则调用全局的操作符new()。
ps->iStack::iStack(512);
return ps;
2.数组的动态分配
仍然参考c++ primer:
new 表达式也可以在空闲存储区中分配数组。在这种情况下,new 表达式中的类型指示符后面必须有一对方括号,里面的维数是数组的长度,且该组数可以是一个复杂的表达式。new 表达式返回指向数组第一个元素的指针。例如:
// 分配一个含有 1024 个元素的数组
// 未被初始化
int *pia = new int[ 1024 ];
我的理解:序列大体上等价:
int array[1024]; //这句代码其实是由调用库操作符new[]()完成的,且该操作符位于全局命名空间。当然,分配的内存空间是在位于程序的自由存储区中的。
int *pia = array;
我的理解:同理对于类对象:
iStack *psa = new iStack[10];
序列大体上等价于伪码:
iStack *psa = (iStack*)malloc(sizeof(iStack)*10); //这句代码其是是由调用类的操作符new[]()完成的,如果重载了就调用重载的,如果没有则调用全局的操作符new[]()。
for(int i=0;i<10;i++)
{
psa[i]->iStack::iStack();
}
return psa;
3.定位new表达式
primer c++你懂得:
new 表达式的第三种形式可以允许程序员要求将对象创建在已经被分配好的内存中。这种形式的 new 表达式被称为定位 new 表达式(placement new expression)。程序员在 new 表达式中指定待创建对象所在的内存地址。new 表达式的形式如下:
new (place_address) type -specifier
place_address 必须是个指针。为了使用这种形式的 new 表达式 我们必须包含头文件<new>。这项设施允许程序员预分配大量的内存,供以后通过这种形式的 new 表达式创建对象。例如:
// 预分配内存 但没有 Foo 对象
char *buf = new char[ sizeof(Foo) * chunk ];
// 在 buf 中创建一个 Foo 对象
Foo *pb = new (buf) Foo;
我的理解:序列大体上等价于伪码:
Foo *pb = (Foo*)buf; //这句代码其实是由调用类的操作符new()完成的,当然是定位形式的操作符new,如果重载了就调用重载的,如果没有则调用全局的操作符new()
pb->Foo::Foo();
return pb;
对内置类型同理,不再累述。
二、探索
首先介绍我的编码环境:ubuntu 12.4 LTS, g++ 4.6.3, eclipse CDT
1.定义在哪
到现在为止,我已经了解了new表达式的3种形式以及其功能过程了,那么剩下最关键的当然就是上面常常提到的“操作符new”了。我们知道如果不进行重载,那么所以调用都是使用库操作符new()或new[]()。哈哈,其实没啥神秘的,它们的定义就在一个头文件里,“/usr/include/c++/4.6/new” 文件乃所求也。
其中关键内容:
//全局操作符new与new[]。被调用时size_t类型参数会被自动初始化为所需的内存大小,单位为字节
void* operator new(std::size_t) throw (std::bad_alloc);
void* operator new[](std::size_t) throw (std::bad_alloc);
//全局操作符new与new[],定位new表达式。被调用时size_t类型参数会被自动初始化为所需的内存大小,单位为字节,__p则代表定位表达式传入括号内的指针
inline void* operator new(std::size_t, void* __p) throw() { return __p; }
inline void* operator new[](std::size_t, void* __p) throw() { return __p; }
2.实现呢
首先,感谢gnu开源,不然毛都看不到。
恩,说正题,定位new表达式就不用找了,就在上面的头文件中。
void* operator new(std::size_t) throw (std::bad_alloc) 实现在gcc的new_op.cc中,可以去“http://gcc.gnu.org/viewcvs/gcc/trunk/libstdc%2B%2B-v3/libsupc%2B%2B/new_op.cc?view=markup”看,关键内容:
_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)
{
void *p;
/* malloc (0) is unpredictable; avoid it. */
if (sz == 0)
sz = 1;
p = (void *) malloc (sz);
while (p == 0)
{
new_handler handler = std::get_new_handler ();
if (! handler)
_GLIBCXX_THROW_OR_ABORT(bad_alloc());
handler ();
p = (void *) malloc (sz);
}
return p;
}
void* operator new[](std::size_t) throw (std::bad_alloc) 实现在gcc的new_opv.cc中,可以去“http://gcc.gnu.org/viewcvs/gcc/trunk/libstdc%2B%2B-v3/libsupc%2B%2B/new_opv.cc?view=markup”看,关键内容:
_GLIBCXX_WEAK_DEFINITION void*
operator new[] (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)
{
return ::operator new(sz);
}
三、代码小例子
#include <cstdio>
#include <new> //为使用定位new表达式
using namespace std;
class TestNew
{
public:
TestNew()
{
m_val = 10086;
}
//new的三种形式是可以重载的,去掉重载后,会恢复调用全局的new运算符
//1.
void* operator new (size_t par)
{
printf("TestNew overload operator new is called.\n");
return ::operator new(par);
}
//2.
void* operator new [](size_t par)
{
printf("TestNew overload operator new [] is called.\n");
return ::operator new[](par);
}
//3.
void* operator new(size_t par,void * p)
{
printf("TestNew overload placement operator new is called.\n");
return ::operator new(par,p);
}
int m_val;
};
int main(int argc, char *argv[])
{
//new的三种形式
//1.单个对象的动态分配
int * p_int = NULL;
p_int = new int(10086);
//2.数组的动态分配
int * p_int_array = NULL;
p_int_array = new int[10];
for(int i = 0;i<10;i++)
{
p_int_array[i] = i;
}
//3.定位new表达式
char * p_int_buf = NULL;
p_int_buf =new char [sizeof(p_int_buf) * 2];
int * p_int_placement = NULL;
p_int_placement = new (p_int_buf) int;
*p_int_placement = 10086;
printf("After new:\n");
printf("*p_int is %d\n",*p_int);
printf("p_int_array has : ");
for(int i = 0;i<10;i++)
{
printf("%d ",p_int_array[i]);
}
printf("\n");
printf("*p_int_placement is %d\n",*p_int_placement);
delete p_int;
delete p_int_array;
delete p_int_buf;
//new的三种形式
//1.单个对象的动态分配
TestNew * p_testnew = NULL;
p_testnew = new TestNew;
//2.数组的动态分配
TestNew * p_testnew_array = NULL;
p_testnew_array = new TestNew[10];
//3.定位new表达式
char * p_testnew_buf = NULL;
p_testnew_buf = new char[sizeof(TestNew)*2];
TestNew * p_testnew_placement = NULL;
p_testnew_placement = new (p_testnew_buf) TestNew;
printf("After new:\n");
printf("p_testnew->m_val is %d\n",p_testnew->m_val);
printf("p_testnew_array has : ");
for(int i = 0;i<10;i++)
{
printf("%d ",p_testnew_array[i].m_val);
}
printf("\n");
printf("p_testnew_placement->m_val is %d\n",p_testnew_placement->m_val);
delete p_testnew;
delete p_testnew_array;
delete p_testnew_buf;
return 0;
}
四、后记
终于整理完了笔记,高兴。当然若各位看官若觉得有啥错误,还望指出,毕竟求真务实才是程序员的本性啊。
浙公网安备 33010602011771号