Q1:new运算符
• new运算符是由两个步骤组成
1. 通过适当的new运算符函数的实例,配置所需要的内存
2. 给配置得来的对象设立初值
Eg:
int * pt = new int(5);
//实际操作如下:
int * pt;
if(pt = _new(sizeof(int)))
*pi = 5;
//通过构造函数配置类对象的方式相同:
Point * pt = new Point;
//实际操作如下:
Point * pt;
if(pt = _new(sizeof(Point)))
pt = Point::Point(pt);
• 要求每一次new的调用都必须传回一个独一无二的指针,因此哪怕new申请的空间是0,也会分配一个字节的内存空间,用来返回独一无二的指针
*注:在g++中,new int(0),将返回一个唯一的非空指针,其中存放 “\0”,可用来释放
*注:在VS中,new int(0)将返回一个唯一的非空指针,其内容未定义,可用来释放
• new运算符实例的实现:(通过malloc实现内存分配)
extern void * operator new(size_t size)
{
if(size ==0)
size = 1; //用来确保返回的是独一无二的非空指针
void * last_alloc;
while(!(last_alloc = malloc(size)))
{
if(_new_handler) //允许用户提供属于自己的_new_handler()函数,自行进行内存分配
(*_new_handler)();
else
return 0;
}
return last_alloc;
}
Q2:delete运算符
• delete运算符也分为两个步骤:
若是类类型,则需要先调用析构函数销毁对象
释放内存空间
Eg:
int * pt = new int(4);
delete pt;
//实际执行操作如下:
if(pt != 0)
_delete(pt);
//对类类型指针,先调用析构函数
Point * pt = new Point;
delete pt;
//实际执行操作如下:
if(pt != 0)
{
Point::~Point(pt);
_delete (pt);
}
• delete 作用在指针上后,该地址上的对象不再合法,但是该地址仍然是合法的
• delete 运算符也是以标准的C free()实现的:
external void operator delete(void * ptr)
{
if(ptr)
{
free((char *)ptr);
}
}
Q3:new与 malloc的区别:
1. 性质上:new 是运算符,malloc是函数
2. 功能上:new 分配内存空间并初始化该空间,malloc仅分配内存空间
3. 实现上:new 通过malloc分配空间,malloc即为分配空间的函数
Q4:针对数组的 new 语意
• 当用 new 定义内置类型数组或POD类型数组,new 操作只是简单的内存分配与释放操作,将不会调用vec_new()函数(该函数功能是把默认构造函数作用于数组中每个元素之上)
Eg:
int * p = new int[5];
//真实操作如下:
int * p = (int*)_new(5 * sizeof(int));
• 如果类定义了默认构造函数,则vec_new()就会被调用,配置并构造类对象组成的数组
Eg:
Point * p = new Point[10];
//真实操作如下:
Point *p;
p = vec_new(0, sizeof(Point), 10, &Point::Point(), &Point::~Point());
• 在构造类类型数组过程中,如果想要进行异常处理,需要将析构函数传送给vec_new(),因为如果构造过程中出错,需要将已构造的对象析构并释放,然后才抛出异常
• 出于效率考虑,在调用 delete 运算符时,需要指明维数(但不需要指明元素个数),否则,运算符将该指针作为一个对象看待,并释放该数组中的第一个元素,而其他元素仍然存在
• new 操作返回的指针应记录元素个数,记录元素个数有两个方法:
cookie:为vec_new()所传回的每一个内存块配置一个额外的word,把元素个数放在这个word中
维护一个“联合数组”:该数组中放置指针及其大小
• 对派生类而言:
Eg:
Point * ptr = new Point3d[10];
delet ptr;
希望delete 操作调用 Point 和 Point3d 的析构函数各十次。
• 施行于数组上的析构函数,是根据交给vec_delete()函数的“被删除的指针类型”来调用析构函数的。
*因此,对上例中,将只调用 Point::~Point()
• 只能在程序员层面进行手动调用,而不是依赖语言执行
for(size_t i(0);i < elem_cnt;++i)
{
Point3d * p = (Point3d*)(&(ptr[i]));
delete p;
}
Q5:Placement Operator new的语意
• placement new是重载operator new的一个标准、全局的版本,它不能被自定义的版本代替(不像普通的operator new和operator delete能够被替换成用户自定义的版本)。
• placement new 需要第二个参数,类型为 void*,其函数原型如下:
void * operator new(size_t,void *p)
{
return p;
}
• 使用实例:
Eg:
Point * pt = new Point;
Point * ptn = new(pt)Point;
上述代码将 pt 所指的内存空间放置新产生的 Point 对象,new运算符新增的参数是用来指定内存空间的
• placement new 运算符的功能:既能决定对象放置在哪里,也能保证会执行对象的构造函数
Eg:
Point * ptn = new(pt)Point;
//实际操作如下:
Point * ptn = (Point*)(pt);
if(ptn != 0)
ptn = Point::Point(ptn);
• 需要注意:如果 placement operator 在原已存在的一个对象上构造新的对象,哪怕该对象定义了析构函数,也不会调用其析构函数,需要程序员手动调用
• 指定的内存块的指针类型:要么与新分配对象同类型,要么是新鲜的内存
注:新鲜内存的定义方式:char p = new char[sizof(Point)];
• 对派生类需要注意:
Eg:
struct T
{
int j;
virutal void f();
}
struct TT : public T
{
virtual void f();
}
//注意:此时 T 与 TT 所占空间大小相同
T b;
b.f(); //调用T::f();
b.~T();
new(&b)TT; //在 T 与 TT 所占空间相同时才不会报错
b.f(); //编译器仍然调用 T::f(),而不是TT::f()