前言

本篇来分析new是怎么实现的, 使用c++进行在申请对象的时候用到new, 但是为什么申请对象要用到new, 而不能用malloc, 而有时申请数组的用new或者malloc似乎又都可以, 这里就来分析一下new实现.

new operator, operator new以及placement new

  1. new operator用法

其实new operator我们经常在使用, 就是我们直接向堆申请一块内存大小, 然后对该内存进行构造和析构.

  template<class T> class point
{
T x; T y;
};
point<int> *p = new point<int>[];

这就是new operator的用法. 其实在使用它的时候, 它会做两步事情.

  1. 向堆申请一块大小的内存.

  2. 对其有构造函数的执行构造函数

其实剩下的两种用法就是将 new operator 的两个功能分开做.

  1. operator new用法

operator new申请一块空间, 但是申请完了就什么都不做. 这感觉就很像malloc函数啊. 对, 没错. 其实operator  new就是间接性的调用了 malloc函数. 我们直接来看源码部分

   void* __CRTDECL operator new(size_t const size)
{
for (;;)
{
if (void* const block = malloc(size))
{
return block;
}

if (_callnewh(size) == )
{
if (size == SIZE_MAX)
{
__scrt_throw_std_bad_array_new_length();
}
else
{
__scrt_throw_std_bad_alloc();
}
}
}
}

很清楚的可以看出来5行的确是直接的调用了malloc函数, 然后除了申请的大小判断就没有了, 那为什么我们不直接用malloc函数而要用operator new???  主要是new的封装, 可重载吧, 毕竟我们常说new不是函数, 而是操作符也是有原因的. 接下来就是最后一个了.

  1. placement new用法

placement new是在已经申请的内存上构建对象. 这就是我们调用new的时候会调用对象的构造函数的原因. 有一点, 刚说了可以在已经申请的内存上构建对象, 难不成不只是堆, 连栈上也能构建对象.

这也是我们内存池经常用的方法, 使用placement new在已经申请的内存上构建对象

它的用法就很灵活了.

  int buff[];
int *p = new(buff) int();

这样我们就在已分配空间的buff中重新构建对象了, 传入的buff代表的是地址, 后面括号代表的初始化的值. 这个例子也证实了我们可以在栈中分配对象, 因为buff就在栈中. 而buff[0]与p都指向的同一块地址.

这里就要注意, 我们用buff地址开始申请的对象, 就尽量不要用buff了, 因为buff的数据被重新的修改了, 使用buff可能就会出现奇怪的数据.

 

同时, buff的长度要足够装下对象的大小, 否则就会出现数据覆盖的危险. 这个博主详细的实现了这个问题 .


现在我们来验证一下上面说的吧. 这个一个很简单的程序, 通过gdb调试定位在new的执行, 可以看到

  template<class T>
class point
{
public:
point()
{
}
private:
T x;
};

int main()
{
point<int> *p = new point<int>[];

return ;
}

图片中的第二行, 对应的跳转就是我们最后一行的要跳转的操作符(operator new), 执行了之后在图片中的下一个call就是调用类的构造函数. new操作符的实现是两步就可概括为先申请了空间, 再调用构造函数.

new的重载实现

前面提了几次关于new可以重载, 那怎么实现重载呢? 我们不是说过new是一个运算符吗, 就是重载运算符就行了.

  template<class T>class point
{
public:
point(T i) : i(i)
{
cout << "point constructor" << endl;
}
void *operator new(size_t size, void *p, const string& str)
{
cout << "operator new" << endl;
if (!p)
{
cout << "new" << endl;
return ::operator new(size);
}
return p;
}
private:
T i;
};
char buf[sizeof(point<int>)];
point<int> *pc = new (buf, "first new") point<int>();

以上就是简单的new的重载. 运行时会有一个奇怪的现象, new先执行, 原因就是先要为类分配内存啊, 所以new比构造函数先执行.


总结

解释了new,  同样的, delete的实现也是跟new有很多相似的, delete事先调用析构函数, 然后再调用free函数释放内存, 同样是可以将析构和释放内存分开调用, 也可以进行重载, 这里就不细讲了. 至于为什么new的对象一定要用delete来释放也容易想明白, 因为delete会默认调用其析构函数, 而free仅仅只是释放空间而没有调用析构. 如果是普通变量用new或malloc申请内存都是可以用delete释放, 毕竟没有析构的就默认什么也不做然后释放内存.


参考 : Placement new operator in C++

随机推荐

  1. SQLyog普通版与SQLyog企业版对比分析

    这里,为什么要写这篇博客呢? 对于SQLyog普通版而言,只能复制到不同的数据库里,这样显得麻烦,当然,你也可以用语句来操作达到实现目的. 具体做法: MySQL复制旧表的结构及数据到新表 CREAT ...

  2. 【SCOI 2007】 降雨量

    [题目链接] 点击打开链接 [算法] 线段树 此题细节很多,写程序时要细心! [代码] #include<bits/stdc++.h> using namespace std; #defi ...

  3. win7 32位解决matlab out of memory问题

    由于最近在做DL,matlab load数据时由于内存只有2G,会出现out of memory的情况,网上百度了下都是在xp下打开3GB来解决该问题,但是由于win7没有boot.ini无法在win ...

  4. Python Matplotlib模块--pyplot

    #-*- coding: utf- -*- ''' numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=No ...

  5. Spark 决策树--回归模型

    package Spark_MLlib import org.apache.spark.ml.Pipeline import org.apache.spark.ml.evaluation.Regres ...

  6. [Swift通天遁地]一、超级工具-(8)地图视图MKMapView的常用代理方法

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  7. Akka源码分析-Cluster-Distributed Publish Subscribe in Cluster

    在ClusterClient源码分析中,我们知道,他是依托于“Distributed Publish Subscribe in Cluster”来实现消息的转发的,那本文就来分析一下Pub/Sub是如 ...

  8. Pycharm初始创建项目和环境搭建(解决aconda库文件引入不全等问题)

    1.新建工程 1.选择新建一个Pure Python项目,新建项目路径可以在Location处选择. 2.Project Interpreter部分是选择新建项目所依赖的python库,第一个选项会在 ...

  9. Hadoop回收站及fs.trash参数详解

    前言: Linux系统里,个人觉得最大的不方便之一就是没有回收站的概念.rm -rf很容易造成极大的损失.而在Hadoop或者说HDFS里面,有trash(回收站)的概念,可以使得数据被误删以后,还可 ...

  10. 最短路 Codeforces Round #103 (Div. 2) D. Missile Silos

    题目传送门 /* 最短路: 不仅扫描边,还要扫描点:点有两种情况,一种刚好在中点,即从u,v都一样,那么最后/2 还有一种是从u,v不一样,两种的距离都是l 模板错了,逗了好久:( */ #inclu ...