C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++ opeartor new和placement new

C++中的opeartor new和placement new使用步骤

作者:Z.0103

这篇文章主要介绍了C++中的opeartor new和placement new详解,在很多情况下,placement new的使用方法和其他普通的new有所不同。这里提供了它的使用步骤,需要的朋友可以参考下

new做了哪些:

在c++中,对new的调用时,new完成的工作通常是有以下几步:

调用operator new函数分配出内存待用对象的构造方法构造出对象返回该对象的指针

operator new
(1)只分配所要求的空间,不调用相关对象的构造函数。当无法满足所要求分配的空间时,则
->如果有new_handler,则调用new_handler,否则
->如果没要求不抛出异常(以nothrow参数表达),则执行bad_alloc异常,否则
->返回0
(2)可以被重载
(3)重载时,返回类型必须声明为void*
(4)重载时,第一个参数类型必须为表达要求分配空间的大小(字节),类型为size_t
(5)重载时,可以带其它参数

opeartor new重载测试

class X
{
public:
    X() { cout<<"constructor of X"<<endl;}
    ~X() { cout<<"destructor of X"<<endl;}
    void* operator new(size_t size,string str)
    {
        cout<<"operator new size "<<size<<" with string "<<str<<endl;
        return ::operator new(size);
    }

    void operator delete(void* pointee)
    {
        cout<<"operator delete"<<endl;
        ::operator delete(pointee);		//调用全局的 operator delete函数
    }
private:
    int num;
};

int main()
{
    X *px = new("A new") X;
    delete px;
    return 0;
}

// 输出结果
// operator new size 4 with string A new
// constructor of X
// destructor of X
// operator delete

那么为什么要重载operator new函数呢?首先第一点当然是因为new作为一种操作符是不能被重载的,而作为new中重要的一环:分配内存,重载operatir new就变的一种必要的操作了。全局opeartor new在分配内存时,实际上也是对malloc的一层包装,在进行大量次数的内存分配时容易出现内存碎片的问题,通过重载operator new函数可以自定的将内存分配在独立出来的一块内存区域,可以更高效率的实现内存分配的管理,同时也可以有效减少内存碎片化以及不易与管理的问题。

重载operator new函数还有很多的用途,比如,他可以帮助我们查找内存泄漏,在c++中,内存泄漏是痛中之痛,重载该函数并与宏定义相配合可以很好的检测出内存泄漏的地方在哪里。相关文章链接:https://www.jb51.net/article/41939.htm

placement new

placement new是operator new的一个重载版本,他的作用是可以将一个对象分配到指定的内存空间。实现代码如下:

void* operator new(std::size_t, void* __p) throw()
{
    return __p;
}

placement new 只是operator new的一个重载版本,只是起了一个别名而已.
代码示例:

class A{
    int num;
public:
    A(){
        cout<<"A's constructor"<<endl;
    }

    ~A(){
        cout<<"~A"<<endl;
    }
    void show(){
        cout<<"num:"<<num<<endl;
    }
};


int main()
{
	char* mem;
	cout << (void*)mem << endl;
	A* a = new(mem) A;
	cout << (void*)a << endl;
	return 0;
}

// 0x104e570ac
// A's constructor
// 0x104e570ac

阅读以上程序,注意以下几点。
(1)用定位放置new操作,既可以在栈(stack)上生成对象,也可以在堆(heap)上生成对象。如本例就是在栈上生成一个对象。

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

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

(4)万不得已才使用placement new,只有当你真的在意对象在内存中的特定位置时才使用它。例如,你的硬件有一个内存映像的I/O记时器设备,并且你想放置一个Clock对象在哪那个位置。

在一些高效率的程序中,往往会开辟出一块独立的内存空间配合placement new来实现高效率的内存释放与配置,如在SGI STL的内存配置函数allocate 与 deallcate函数,以及大名鼎鼎的memory pool技术都很大程度上依靠了placement new定位创建对象。在快速的内存分配与释放的过程中这是一个非常实用的方法。

Placement new使用步骤

在很多情况下,placement new的使用方法和其他普通的new有所不同。这里提供了它的使用步骤。

第一步 缓存提前分配

有三种方式:

1.为了保证通过placement new使用的缓存区的memory alignment(内存队列)正确准备,使用普通的new来分配它:在堆上进行分配

class Task ;
char * buff = new [sizeof(Task)]; //分配内存
(请注意auto或者static内存并非都正确地为每一个对象类型排列,所以,你将不能以placement new使用它们。)

2.在栈上进行分配

class Task ;
char buf[N*sizeof(Task)]; //分配内存

3.还有一种方式,就是直接通过地址来使用。(必须是有意义的地址)

void* buf = reinterpret_cast<void*> (0xF00F);

第二步:对象的分配

在刚才已分配的缓存区调用placement new来构造一个对象。

Task *ptask = new (buf) Task

第三步:使用

按照普通方式使用分配的对象:

ptask->memberfunction();
ptask-> member;
//...

第四步:对象的析构

一旦你使用完这个对象,你必须调用它的析构函数来毁灭它。按照下面的方式调用析构函数:

ptask->~Task(); //调用外在的析构函数,显式调用!

第五步:释放

你可以反复利用缓存并给它分配一个新的对象(重复步骤2,3,4)如果你不打算再次使用这个缓存,你可以象这样释放它:

delete [] buf;

总结:

(1)若想在堆上建立一个对象,应该用new操作符。它既分配内存又调用其构造函数进行初始化。
(2)若仅仅想分配内存,应该调用operator new(),他不会调用构造函数。若想定制自己在堆对象被建立时的内存分配过程,应该重写自己的operator new()。
(3)若想在一块已经获得的内存空间上建立一个对象,应该用placement new。在实际开发过程中,这种写法一般在高性能高稳定场景下使用。本文主要是为了更好的理解STL源码中alloator的内存管理行为所写。

理解可能并不规范,表达也会有所纰漏。

参考文章:

https://cloud.tencent.com/developer/article/1177460

https://blog.51cto.com/u_15060533/4689267

https://www.cnblogs.com/luxiaoxun/archive/2012/08/10/2631812.html

到此这篇关于C++中的opeartor new和placement new详解的文章就介绍到这了,更多相关C++ opeartor new和placement new内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文