700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > c++ new操作符(new operator) operator new placement new operator new[

c++ new操作符(new operator) operator new placement new operator new[

时间:2022-01-12 10:29:17

相关推荐

c++ new操作符(new operator) operator new placement new  operator new[

一.new

new operator就是new操作符,不能被重载,假如A是一个类,那么A * a=new A;实际上执行如下3个过程。

(1)调用operator new分配内存,operator new (sizeof(A))

(2)调用构造函数生成类对象,A::A()

(3)返回相应指针

事实上,分配内存这一操作就是由operator new(size_t)来完成的,如果类A重载了operator new,那么将调用A::operator new(size_t ),否则调用全局::operator new(size_t ),后者由C++默认提供。

C++中虽然不允许定义长度为0的数组,但明确指出 动态分配new 长度为0的数组 是合法的,他返回的是一个合法的非0指针,这个指针与一般new返回的指针有所不同,他不能进行读(解引用)操作(如果进行解引用操作,那么结果是undefined 的)。更不能进行写操作,不然调用delete函数时,会报错崩溃。

但 如果是 如 int a[0] 这样就会直接报错。

Widget* pw = new Widget; //使用正常签名式的new创建对象delete pw;

对于使用正常签名式的new来创建对象时,其可能在两个地方会抛出异常:①当在调用new()时,new()函数中可能会抛出异常②如果new()函数没有抛出异常,但是后面对象的构造函数可能会抛出异常。系统会自动调用delete()来释放new()所做的一切,使其恢复原状(释放内存等)。分析:构造函数抛出异常,指针尚未被赋值,客户端无法取得该指针归还内存,因此此处的内存归还操作就交给了C++运行期系统身上。注意:该delete操作不会调用对象的析构函数(前提是要处理掉异常),只释放内存。

二.operator new

operator new是函数,分为三种形式(前2种不调用构造函数,这点区别于new operator):

void* operator new (std::size_t size) throw (std::bad_alloc);

void* operator new (std::size_t size, const std::nothrow_t& nothrow_constant) throw();

void* operator new (std::size_t size, void* ptr) throw();

第一种分配size个字节的存储空间,并将对象类型进行内存对齐。如果成功,返回一个非空的指针指向首地址。失败抛出bad_alloc异常。

第二种在分配失败时不抛出异常,它返回一个NULL指针。

第三种是placement new版本,它本质上是对operator new的重载,定义于#include <new>中。它不分配内存,调用合适的构造函数在ptr所指的地方构造一个对象,之后返回实参指针ptr。

第一、第二个版本可以被用户重载,定义自己的版本,第三种placement new不可重载。

A* a = new A; //调用第一种

A* a = new(std::nothrow) A; //调用第二种

new (p)A(); //调用第三种

new (p)A()调用placement new之后,还会在p上调用A::A(),这里的p可以是堆中动态分配的内存,也可以是栈中缓冲。

注意:

operator new 函数会被子类继承的,所以参数size 的大小要注意。

三.placement new

一般来说,使用new申请空间时,是从系统的“堆”(heap)中分配空间。申请所得的空间的位置是根据当时的内存的实际使用情况决定的。但是,在某些特殊情况下,可能需要在已分配的特定内存创建对象,这就是所谓的“定位放置new”(placement new)操作。

定位放置new操 作的语法形式不同于普通的new操作。例如,一般都用如下语句A* p=new A;申请空间,而定位放置new操作则使用如下语句A* p=new (ptr)A;申请空间,其中ptr就是程序员指定的内存首地址.

#include <iostream>using namespace std;class A{public:A(int n){cout << "A's constructor" << endl;num = n;}~A(){cout << "A's destructor" << endl;}virtual void show(){cout << "num:" << num << endl;}private:int num;};int main(){char mem[100];mem[0] = 'A';mem[1] = '\0';mem[2] = '\0';mem[3] = '\0';cout << (void*)mem << endl;A* p = new (mem)A(2);cout << p << endl;p->show();p->~A();getchar();}

注意以下几点。

(1)用定位放置new操作,既可以在栈(stack)上生成对象,也可以在堆(heap)上生成对象。如本例就是在栈上生成一个对象。

(2)使用语句A* p=new (mem) A;定位生成对象时,指针p和数组名mem指向同一片存储区。所以,与其说定位放置new操作是申请空间,还不如说是利用已经请好的空间,真正的申请空间的工作是在此之前完成的。

(3)使用语句A *p=new (mem) A;定位生成对象时,会自动调用类A的构造函数,但是由于对象的空间不会自动释放(对象实际上是借用别人的空间),所以必须显示的调用类的析构函数,如本例

在堆上:

class EquipmentPiece {public:EquipmentPiece(int IDNumber) {}virtual ~EquipmentPiece() {}int a = 1;float b = 2.0;};

void* rawMemorysingle = operator new(sizeof(EquipmentPiece));EquipmentPiece* bestPiecesrawMemorysingle = static_cast<EquipmentPiece*> (rawMemorysingle);new(bestPiecesrawMemorysingle) EquipmentPiece(1);bestPiecesrawMemorysingle->~EquipmentPiece();operator delete(rawMemorysingle);

更近一步:

//调用placement new创建对象Widget* pw = new (std::cerr) Widget;//调用正常签名式的delete删除对象delete pw;

1.如果placement new未抛出异常,那么不会有任何事情

2.如果placement new未抛出异常,而当Widget的构造函数抛出异常时。需要自定义好placement delete 函数,这样系统就会自动调用placement delete函数释放内存。

因为:此时pw指针,pw指针尚未被赋值,客户端无法取得该指针归还内存(此时出现的情况于“一”中的演示案例一样)。

但是此时我们使用的是placement new创建的对象,因此C++系统回去查找是否有placement delete来进行恢复原状,但是Widget没有定义,因此运行期系统不知道如何取消并恢复原先对placement new的调用

注意:如果没有抛出异常,delete pw;调用的是正常形式的operator delete,而不是placement delete.

四、delete 操作符

A * a=new A;

delete a;

delete也分两部分的操作。

(1)首先调用A::~A()将对象析构

(2)调用::operator delete释放内存

如果在对象的析构函数出现异常,并且不通过try catch 捕捉。那么系统不会自动调用::operator delete释放内存。结果是析构函数异常后面的代码都不执行,对象本身内存也不释放,出现内存泄漏。

处理办法:析构函数不能出现异常,如果出现异常,必须使用try catch 捕捉吞掉,不让异常套路析构函数。

延伸:

a.如果只想处理未被初始化的内存,应该绕过new和delete 操作符,而是调用operator new 获取内存和operator delete释放内存给系统。

b.如果你用placement new在内存中建立对象,你应该避免在该内存中用delete操作符。因为delete操作符调用operator delete来释放内存,但是包含对象的内存最初不是被operator new分配的,placement new只是返回转到给它的指针。而是应该显性调用对象的析构函数来解除析构函数的影响。

class EquipmentPiece {public:EquipmentPiece(int IDNumber) {}virtual ~EquipmentPiece() {}int a = 1;float b = 2.0;};

void* rawMemorysingle = operator new(sizeof(EquipmentPiece));EquipmentPiece* bestPiecesrawMemorysingle = static_cast<EquipmentPiece*> (rawMemorysingle);new(bestPiecesrawMemorysingle) EquipmentPiece(1);bestPiecesrawMemorysingle->~EquipmentPiece();operator delete(rawMemorysingle);

五、new A[] (即new 数组)

1.先调用operator new[ ]分配一个 连续的内存块。

2.对每个数组元素(即对象)调用缺省(或无参数)的构造函数。

如:string *ps = new string[10];

同理 delete [ ]A; 如: delete [ ]ps;

1.先为每个数组元素调用析构函数

2.调用 operator delete [] 释放内存。

class EquipmentPiece {public:EquipmentPiece(int IDNumber) {}virtual ~EquipmentPiece() {}int a = 1;float b = 2.0;};

void* rawMemory = operator new[](10 * sizeof(EquipmentPiece));EquipmentPiece* bestPieces6 = static_cast<EquipmentPiece*>(rawMemory);for (int i = 0; i < 10; ++i)new(&bestPieces6[i]) EquipmentPiece(i);for (int i = 9; i >= 0; --i)bestPieces6[i].~EquipmentPiece(); // 如果使用普通的数组删除方法,程序的运行将是不可预测的operator delete[](rawMemory);

扩展:

1.在C++中有一条规则是每一个重载的operator必须带有一个用户定义类型(user-defined type)的参数

c++ new操作符(new operator) operator new placement new operator new[] 及相对应的delete 操作符 operator delete

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。