关于new/delete,援引C++ Primer中的一段话:

  某些应用程序对内存分配有特殊的要求,因此我们无法直接将标准的内存管理机制直接应用于这些程序。他们常常需要自定义内存分配的细节,比如使用关键字new将对象放置在特定的内存空间中。为了实现这一目的,应用程序需要重载new运算符和delete预算符以实现内存分配的过程。在讲new/delete系列的重载之前,我们先要明确堆对象构造与析构的过程。

  讲真,对于new/delete的重载,在之前一直不觉得其有什么实质性的作用,直到我加入了一现在的一家公司做数据通信才碰到。

new 或者 new[ ]的作用:

当我们使用一条new表达式时:

string *pstr     = new string("test");
string *pstrArr = new string[];

实际执行了三部操作:

1)new表达式调用一个名为operator new(或者operator new[ ])的标准库函数。该函数的作用是分配一块足够大的内存空间以便存储特定类型的对象。

2)编译器运行相应的构造函数初始化这块内存。

3)对象被分配空间完成,返回一个带类型的指针指向这块个象。

delete delete[ ]的作用

delete pstr;
delete [] pstrArr;

1)调用指针所指对象执行析构函数。

2)编译器调用operator delete(或者operator delete[ ])的标准函数释放内存。

new/delete默认重载

C++默认的几种重载模型如下

//标准版本(常用)
void *operator new(size_t,nothrow &);
void *operator new[](size_t,nothrow &);
void *operator delete(void *,nothrow &) noexcept;
void *operator delete(void *,nothrow &) noexcept;

//不会抛出异常的版本
void *operator new(size_t,nothrow &) noexcept;
void *operator new[](size_t,nothrow &) noexcept;
void *operator delete(void *,nothrow &) noexcept;
void *operator delete(void *,nothrow &) noexcept;
#include <iostream>
#include <new> using namespace std; int main(int argc, char *argv[])
{
double * ptr[];
for(int i=; i<; i++)
{
ptr[i] = new double[];
if(ptr[i] == NULL)
{
cout<<"new error"<<endl;
exit();
} cout<<"ptr["<<i<<"]"<<"->50000000 duble"<<endl;
}
return ;
}

运行结果:

#include <iostream>
#include <new> using namespace std; int main(int argc, char *argv[])
{
double * ptr[];
for(int i=; i<; i++)
{
ptr[i] = new (nothrow)double[];
if(ptr[i] == NULL)
{
cout<<"new error"<<endl;
exit();
} cout<<"ptr["<<i<<"]"<<"->50000000 duble"<<endl;
}
return ;
}

运行结果:

模仿默认实现重载

以上函数既可以全局重载,也可以成员重载,全局重载不常用,下面是成员重载的方式,全局的重载只需要将他们拷贝到类外就好了。

#include <iostream>

using namespace std;

class A
{
public:
A(){
cout<<"A constructor"<<endl;
}
~A(){
cout<<"A destructor"<<endl;
} void * operator new (size_t size)
{
cout<<"new "<<size<<endl;
return malloc(size);
} void operator delete(void *p)
{
cout<<"delete"<<endl;
free(p);
} void * operator new[] (size_t size) noexcept
{
cout<<"new[] "<<size<<endl;
return malloc(size);
} void operator delete[](void *p) noexcept
{
cout<<"delete[] "<<endl;
free(p);
} void dis()
{
cout<<"dis"<<endl;
} private:
int a;
double d;
}; using Func = void (*)(); int main(int argc, char *argv[])
{
cout<<"sizeof(A):"<<sizeof(A)<<endl; cout<<"\n---new A---"<<endl;
A * newA = new A; cout<<"---new A[1]---"<<endl;
A * newA1 = new A[]; cout<<"\n---new A[2]---"<<endl;
A * newA2 = new A[]; cout<<"\n---delete A---"<<endl;
delete newA; cout<<"\n---delete A[1]---"<<endl;
delete newA1; cout<<"\n---delete A[2]--"<<endl;
delete [] newA2; return ;
}

运行结果:

new[ ]分配内存大小与期望不一致的问题

调用new[]函数的时候,其实际生成的内存大小与对象个数并不成比例,

比如A *p = new A[10];

实际上生成的内存大小并非sizeof(A) * 10,还要生成一个内存大小计数

这个要归咎于自定义对象是否自定义了析构函数,如果没有自定义析构函数,那么编译器会优化内存申请,不会多出四个字节。

详情请查看这篇博客:

https://www.cnblogs.com/tp-16b/p/8684298.html

关于C++内存操作符重载的特别声明

1)   隐式静态,成员重载不可在函数操作数据成员变量(加 static修饰也不会有问题)。

2)operator new/operator new[ ]第一个参数必须是size_t类型,且不能含有默认实参。

3)operator new/operator new[ ]重载时,可以提供额外的参数,使用时必须使用new的定位形式将实参传给定位的形参。

4)void * new(size_t , void *)这个函数只有系统提供的版本,不可重载。

5)operator delete/operator delete[ ]第一个形参必须是void *,该函数还可以包含一个size_t的形参。

new[1] 与new 的不同

operator new /new[ ]  /// operator delete /delete[ ]与其他运算符重载的异同

operator new 和 operator delete实际上是和其他的operator函数(如operator =)是不一样的,这两个函数并没有重载new 或者 delete表达式,实际上我们根本无法自定义表new或者delete达式的行为。

我们提供的operator new 函数和 operator delete函数覅热目的在于改变内存的分配方式,当不管怎么样不能改变new运算符和delete运算符的基本含义。

占位符问题:

既然说到了new/delete重载,那么我们就说一下占位符的问题。

空的结构体/空类默认有一个占位符。所以默认大小是一个字节。

例如:

#include <iostream>

using namespace std;

class EmptyClass
{}; struct EmptyStruct
{}; class EmptyClassWiFlag
{
char ch[];
}; struct EmptyStructWithFlg
{
char ch[];
}; int main(int argc, char *argv[])
{
cout<<"sizeofClass:"<<sizeof(EmptyClass)<<endl;
cout<<"sizeofClass:"<<sizeof(EmptyStruct)<<endl;
cout<<"sizeofClass:"<<sizeof(EmptyClassWiFlag)<<endl;
cout<<"sizeofClass:"<<sizeof(EmptyStructWithFlg)<<endl;
return ;
}

那么当我们在一个空类中声明一个类型的数组,并且数组元素个数设置为0时他就真的成了一个空类。

这种真正的空类有什么用呢?

如上我们有时会重载operator new / operator new [ ] ,有时我们会根据项目需求申请超出类大小的内存。那么如何拿到这个类对象还没用到的内存就是一个问题。char [0]这时就派上用场了。

这里重载一个new的定位形式placer new,来展示空占位符的作用:

#include <iostream>
#include <string.h> using namespace std; class EmptyClassWiFlag
{
public:
EmptyClassWiFlag()
:a()
{}
~EmptyClassWiFlag()
{}
void* freeMemBegain()
{
return ch;
}
private:
int a;
char ch[];
}; void* operator new(size_t memSize,size_t reqMemSize)
{
cout<<"memsize:"<<memSize<<endl;
cout<<"reqMemSize:"<<reqMemSize<<endl;
return malloc(reqMemSize);
} #define MEMCOUNT 100
int main(int argc, char *argv[])
{
EmptyClassWiFlag * p1 = new (MEMCOUNT)EmptyClassWiFlag;
cout<<"addrOfP1:"<<p1<<endl;
cout<<"freeMemBegainAddr:"<<p1->freeMemBegain()<<endl;
memset(p1->freeMemBegain(),0x00,MEMCOUNT-sizeof(EmptyClassWiFlag));
char *flag = "hello world";
memcpy(p1->freeMemBegain(),flag,strlen(flag)+);
cout<<(char*)p1->freeMemBegain()<<endl;
return ;
}

成员变量char ch[0]起到了对象占用内存截至,剩余内存开头的作用。

运算符重载之new与delete的更多相关文章

  1. C++运算符重载 模板友元 new delete ++ = +=

    今天的重载是基于C++ 类模板的,如果需要非类模板的重载的朋友可以把类模板拿掉,同样可以参考,谢谢. 一.类模板中的友元重载 本人喜好类声明与类成员实现分开写的代码风格,如若您喜欢将类成员函数的实现写 ...

  2. 深入C++的运算符重载

    对于简单的运算符,可以参考之前的博文.之后会有一篇关于从等号运算符重载的角度研究深浅拷贝的博文.这里是讲:逗号,取成员运算符,输入输出运算符,下标运算符,括号,new和delete的重载. 逗号运算符 ...

  3. 类型转换运算符、*运算符重载、->运算符重载、operator new 和 operator delete

    一.类型转换运算符 必须是成员函数,不能是友元函数 没有参数 不能指定返回类型 函数原型:operator 类型名();  C++ Code  1 2 3 4 5 6 7 8 9 10 11 12 1 ...

  4. C++运算符重载

    C++运算符重载 基本知识 重载的运算符是具有特殊名字的函数,他们的名字由关键字operator和其后要定义的运算符号共同组成. 运算符可以重载为成员函数和非成员函数.当一个重载的运算符是成员函数时, ...

  5. 标准C++之运算符重载和虚表指针

    1 -> *运算符重载 //autoptr.cpp     #include<iostream> #include<string> using namespace std ...

  6. c/c++面试题(6)运算符重载详解

    1.操作符函数: 在特定条件下,编译器有能力把一个由操作数和操作符共同组成的表达式,解释为对 一个全局或成员函数的调用,该全局或成员函数被称为操作符函数.该全局或成员函数 被称为操作符函数.通过定义操 ...

  7. 实验12:Problem H: 整型数组运算符重载

    Home Web Board ProblemSet Standing Status Statistics   Problem H: 整型数组运算符重载 Problem H: 整型数组运算符重载 Tim ...

  8. C++学习26 运算符重载的概念和语法

    所谓重载,就是赋予新的含义.函数重载(Function Overloading)可以让一个函数名有多种功能,在不同情况下进行不同的操作.运算符重载(Operator Overloading)也是一个道 ...

  9. C++运算符重载——重载特殊运算符

    1.重载赋值运算符= 赋值运算符用于同类对象间的相互赋值.赋值运算符只能被重载为类的非静态成员函数,不能重载为友元函数和普通函数. 对于用户自定义的类而言,如果没有重载赋值运算符,那么C++编译器会为 ...

随机推荐

  1. WUSTOJ 1283: Hamster(Java)

    1283: Hamster 参考博客 wust_tanyao的博客 题目   第0个月有1对仓鼠.仓鼠的寿命是M个月,仓鼠成年后每个月生一对仓鼠(一雌一雄),问N个月后有仓鼠多少对.更多内容点此链接 ...

  2. 关于spring中配置文件路径的那些事儿

    在项目中我们经常会需要读一些配置文件来获取配置信息,然而对于这些配置文件在项目中存放的位置以及获取这些配置文件的存放路径却经常搞不清楚,自己研究了一下,记录下来以备后用. 测试代码如下 package ...

  3. (三)自定义Realm

    一.Realm概念 Realm:域,Shiro从从Realm获取安全数据(如用户.角色.权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用 ...

  4. (十一)SpringBoot之文件上传以及

    一.案例 1.1 配置application.properties #主配置文件,配置了这个会优先读取里面的属性覆盖主配置文件的属性 spring.profiles.active=dev server ...

  5. Nature Biotechnology:人类基因研究走近平民 数据是基础解读更重要

    Nature Biotechnology:人类基因研究走近平民 数据是基础解读更重要 5万美元可以做什么?最近,美国斯坦福大学教授斯蒂芬·夸克在国际著名学术期刊<自然·生物技术>发表论文宣 ...

  6. Tensorflow 训练inceptionV4 并移植

    安装brazel    (请使用最新版的brazel  和最新版的tensorflow  ,版本不匹配会出错!!!) 下载bazel-0.23   https://pan.baidu.com/s/1X ...

  7. Sqlmap对dvwa进行sql注入测试

    前提准备条件: 1.下载安装dvwa,下载链接地址:http://www.dvwa.co.uk/.2.需要安装python运行环境.3.下载sqlmap包并将其解压. 一.查看所有的数据库;(其中db ...

  8. 什么是N+1查询?

    在Session的缓存中存放的是相互关联的对象图.默认情况下,当Hibernate从数据库中加载Customer对象时,会同时加载所有关联的Order对象.以Customer和Order类为例,假定O ...

  9. redis目录

    一.redis基础 二.django-redis 三.redis数据操作详解 四.redis持久化

  10. 《浏览器工作原理与实践》<09>块级作用域:var缺陷以及为什么要引入let和const?

    在前面我们已经讲解了 JavaScript 中变量提升的相关内容,正是由于 JavaScript 存在变量提升这种特性,从而导致了很多与直觉不符的代码,这也是 JavaScript 的一个重要设计缺陷 ...