C++ new

new operator

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++默认提供。

operator new

operator new是函数,分为三种形式,其中前2种不调用构造函数

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();  

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

plain new

顾名思义就是普通的new,就是我们惯常使用的new。在C++中是这样定义的:

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

plain new在分配失败的情况下,抛出异常std::bad_alloc而不是返回NULL,因此通过判断返回值是否为NULL是徒劳的

nothrow new

不抛出异常的运算符new的形式。nothrow new在失败时,返回NULL。定义如下:

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

placement new

这种new允许在一块已经分配成功的内存上重新构造对象或对象数组placement new不用担心内存分配失败,因为它根本不分配内存,它做的唯一一件事情就是调用对象的构造函数。定义如下:

void* operator new(size_t,void*);
void operator delete(void*,void*);
  1. palcement new的主要用途就是反复使用一块较大的动态分配的内存来构造不同类型的对象或者他们的数组。
  2. placement new构造起来的对象或其数组,要显示的调用他们的析构函数来销毁,千万不要使用delete。

一般都用如下语句A* p=new A;申请空间,而定位放置new操作则使用如下语句A* p=new (ptr)A;申请空间,其中ptr就是程序员指定的内存首地址。考察如下程序。

#include <iostream>
using namespace std;
 
class A
{
public:
	A()
	{
		cout << "A's constructor" << endl;
	}
 
 
	~A()
	{
		cout << "A's destructor" << endl;
	}
	
	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;
	cout << p << endl;
	p->show();
	p->~A();
	getchar();
}

以上程序需注意以下几点:

  1. 用placement new操作,既可以在栈(stack)上生成对象,也可以在堆(heap)上生成对象。如本例就是在栈上生成一个对象。
  2. 使用语句A* p=new (mem) A;定位生成对象时,指针p和数组名mem指向同一片存储区。所以,与其说定位放置new操作是申请空间,还不如说是利用已经请好的空间,真正的申请空间的工作是在此之前完成的。
  3. 使用语句A *p=new (mem) A;定位生成对象时,会自动调用类A的构造函数,但是由于对象的空间不会自动释放(对象实际上是借用别人的空间),所以必须显示的调用类的析构函数,如本例中的p->~A()。
  4. 如果有这样一个场景,我们需要大量的申请一块类似的内存空间,然后又释放掉,比如在在一个server中对于客户端的请求,每个客户端的每一次上行数据我们都需要为此申请一块内存,当我们处理完请求给客户端下行回复时释放掉该内存,表面上看者符合c++的内存管理要求,没有什么错误,但是仔细想想很不合理,为什么我们每个请求都要重新申请一块内存呢,要知道每一次内从的申请,系统都要在内存中找到一块合适大小的连续的内存空间,这个过程是很慢的(相对而言),极端情况下,如果当前系统中有大量的内存碎片,并且我们申请的空间很大,甚至有可能失败。为什么我们不能共用一块我们事先准备好的内存呢?可以的,我们可以使用placement new来构造对象,那么就会在我们指定的内存空间中构造对象。

下面是一个在堆上生成对象的例子:

#include <iostream>
using namespace std;
 
class B
{
public:
    B()
    {
        cout<<"B's constructor"<<endl;
    }
 
 
    ~B()
    {
        cout<<"B's destructor"<<endl;
    }
 
 
    void SetNum(int n)
    {
        num = n;
    }
 
 
    int GetNum()
    {
        return num;
    }
 
 
private:
    int num;
};
 
int main()
{
	char* mem = new char[10 * sizeof(B)];
	cout << (void*)mem << endl;
	B *p = new(mem)B;
	cout << p << endl;
	p->SetNum(10);
	cout << p->GetNum() << endl;
	p->~B();
	delete[]mem;
	getchar();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值