new实现
前言
本篇来分析new是怎么实现的, 使用c++进行在申请对象的时候用到new, 但是为什么申请对象要用到new, 而不能用malloc, 而有时申请数组的用new或者malloc似乎又都可以, 这里就来分析一下new实现.
new operator, operator new以及placement new
new operator用法
其实new operator我们经常在使用, 就是我们直接向堆申请一块内存大小, 然后对该内存进行构造和析构.
template<class T> class point
{
T x; T y;
};
point<int> *p = new point<int>[];
这就是new operator的用法. 其实在使用它的时候, 它会做两步事情.
向堆申请一块大小的内存.
对其有构造函数的执行构造函数
其实剩下的两种用法就是将 new operator 的两个功能分开做.
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不是函数, 而是操作符也是有原因的. 接下来就是最后一个了.
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++
随机推荐
- A Go library implementing an FST (finite state transducer)——mark下
https://github.com/couchbaselabs/vellum Building an FST To build an FST, create a new builder using ...
- [翻译]NUnit---Condition Asserts && Comparisons Asserts && Type Asserts (五)
网址:http://www.cnblogs.com/kim01/archive/2013/03/31/2991597.html Condition Asserts 测试指定条件的方法称作条件测试,测试 ...
- [IOI2005]Riv 河流
https://www.zybuluo.com/ysner/note/1300088 题面 有一棵\(n\)个点的树,现在在上面放\(k\)个标记,使得每个点的权值乘上自己到最近的标记祖先的距离的和最 ...
- POJ1584 A Round Peg in a Ground Hole 凸包判断 圆和凸包的关系
POJ1584 题意:给定n条边首尾相连对应的n个点 判断构成的图形是不是凸多边形 然后给一个圆 判断圆是否完全在凸包内(相切也算) 思路:首先运用叉积判断凸多边形 相邻三条边叉积符号相异则必有凹陷 ...
- Ural 1517. Freedom of Choice 后缀数组
Ural1517 所谓后缀数组, 实际上准确的说,应该是排序后缀数组. 一个长度为N的字符串,显然有N个后缀,将他们放入一个数组中并按字典序排序就是后缀数组的任务. 这个数组有很好的性质,使得我们运行 ...
- 深入理解JMM(Java内存模型) --(五)锁
锁的释放-获取建立的happens before 关系 锁是Java并发编程中最重要的同步机制.锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息. 下面是锁释放-获取的示例代 ...
- 从csv文件读取数据到二维vector
void ReadDataFromCsv(std::string &filename, std::vector<std::vector<std::string> > & ...
- jQuery:has()和jQuery:contains()及jQuery:empty
jQuery:has()和jQuery:contains()两个方法比较类似.不同点在于: has是判断标签的 contains是判断文本的 1.jQuery:has() <div>< ...
- 05_锅炉压力案例_java实现
最开始是有机顶盒,后来电视里面都带着一个安卓的设备.现在空调.冰箱.洗衣机都跃跃欲试地想把这个东西加上.海尔出了一款智能冰箱,就是在冰箱门上嵌了一个12寸的屏幕.这个屏幕实际上就是一个安卓的平板似的. ...
- CodeForces 382C Arithmetic Progression (排序+分类讨论)
题意:给出一个长度为n的序列,表示有n张卡片,上面的数字,现在还有一张卡片,上面没有数字,问说可以写几种数字在这张卡片上面, 使得n+1张卡片上的数字可以排列成一个等差数列,有无限多种时输出-1. 析 ...