placement new的用法及用途


placement new 是重载operator new的一个标准,全局的版本,他不能被自定义的版本代替(不像普通的opertor new和opertor delete能够被替换成用户自定的版本)。

原型:
void *operator new(size_t, void *p)throw() return { p;}

区分关键字: new, operator new, placement new
new和delete操作符:对堆中的内存进行申请的释放, 这两个是不能被重载的;
而要实现不同的内存分配行为,需要重载operator new, 而不是new和delete。

new
class Myclass{...};
Myclass *p = new Myclass;
这里new实际上是执行了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是函数,分三种形式(前两种不调用构造函数,这点区别于new operator):
void* operator new(std::size_t, size)throw(std::bad_alloc);
void* operator new(std::size_t, const std::nothrow_t& nothrow_constant) throw();
void* operator new(std::size_t, 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可以是堆中动态分配的内存,也可以是栈中缓冲。

#include <iostream>
using namespace std;


class A{
 public:
    A(){
        cout << "A is a constructor" << endl;
    }
    ~A(){
        cout << "A is a destructor" << endl;
    }
    void show(){
        cout << "num = " << num << endl;
    }
    
private:
    int num;
};

int main (){
    int num;
    num = 10;
    cout << &num << endl;
    A* p = new (&num)A();
    cout << p << endl;
    p->show();
    p->~A();
    return 0;
}


(1)用定位放置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来构造对象,那么就会在我们指定的内存空间中构造对象。

全部评论

相关推荐

不愿透露姓名的神秘牛友
07-09 12:02
ssob上原来真有BOSS啊
硫蛋蛋:这种也是打工的,只不是是给写字楼房东打工
点赞 评论 收藏
分享
06-13 17:33
门头沟学院 Java
顺序不记了,大致顺序是这样的,有的相同知识点写分开了1.基本数据类型2.基本数据类型和包装类型的区别3.==和equals区别4.ArrayList与LinkedList区别5.hashmap底层原理,put操作时会发生什么6.说出几种树型数据结构7.B树和B+树区别8.jvm加载类机制9.线程池核心参数10.创建线程池的几种方式11.callable与runnable区别12.线程池怎么回收线程13.redis三剑客14.布隆过滤器原理,不要背八股,说说真正使用时遇到了问题没有(我说没有,不知道该怎么回答了)15.堆的内存结构16.自己在写项目时有没有遇见过oom,如何处理,不要背八股,根据真实经验,我说不会17.redis死锁怎么办,watchdog机制如何发现是否锁过期18.如何避免redis红锁19.一个表性别与年龄如何加索引20.自己的项目的QPS怎么测的,有没有真正遇到大数量表21.说一说泛型22.springboot自动装配原理23.springmvc与springboot区别24.aop使用过嘛?动态代理与静态代理区别25.spring循环依赖怎么解决26.你说用过es,es如何分片,怎么存的数据,1000万条数据怎么写入库中27.你说用limit,那么在数据量大之后,如何优化28.rabbitmq如何批次发送,批量读取,答了延迟队列和线程池,都不对29.计网知不知道smtp协议,不知道写了对不对,完全听懵了30.springcloud知道嘛?只是了解反问1.做什么的?短信服务,信息量能到千万级2.对我的建议,基础不错,但是不要只背八股,多去实际开发中理解。面试官人不错,虽然没露脸,但是中间会引导我回答问题,不会的也只是说对我要求没那么高。面完问我在济宁生活有没有困难,最快什么时候到,让人事给我聊薪资了。下午人事打电话,问我27届的会不会跑路,还在想办法如何使我不跑路,不想扣我薪资等。之后我再联系吧,还挺想去的😭,我真不跑路哥😢附一张河科大幽默大专图,科大就是大专罢了
查看30道真题和解析
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
07-10 13:54
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务