运算符重载之new与delete
关于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 *)这个函数只有系统提供的版本,不可重载。
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的更多相关文章
- C++运算符重载 模板友元 new delete ++ = +=
今天的重载是基于C++ 类模板的,如果需要非类模板的重载的朋友可以把类模板拿掉,同样可以参考,谢谢. 一.类模板中的友元重载 本人喜好类声明与类成员实现分开写的代码风格,如若您喜欢将类成员函数的实现写 ...
- 深入C++的运算符重载
对于简单的运算符,可以参考之前的博文.之后会有一篇关于从等号运算符重载的角度研究深浅拷贝的博文.这里是讲:逗号,取成员运算符,输入输出运算符,下标运算符,括号,new和delete的重载. 逗号运算符 ...
- 类型转换运算符、*运算符重载、->运算符重载、operator new 和 operator delete
一.类型转换运算符 必须是成员函数,不能是友元函数 没有参数 不能指定返回类型 函数原型:operator 类型名(); C++ Code 1 2 3 4 5 6 7 8 9 10 11 12 1 ...
- C++运算符重载
C++运算符重载 基本知识 重载的运算符是具有特殊名字的函数,他们的名字由关键字operator和其后要定义的运算符号共同组成. 运算符可以重载为成员函数和非成员函数.当一个重载的运算符是成员函数时, ...
- 标准C++之运算符重载和虚表指针
1 -> *运算符重载 //autoptr.cpp #include<iostream> #include<string> using namespace std ...
- c/c++面试题(6)运算符重载详解
1.操作符函数: 在特定条件下,编译器有能力把一个由操作数和操作符共同组成的表达式,解释为对 一个全局或成员函数的调用,该全局或成员函数被称为操作符函数.该全局或成员函数 被称为操作符函数.通过定义操 ...
- 实验12:Problem H: 整型数组运算符重载
Home Web Board ProblemSet Standing Status Statistics Problem H: 整型数组运算符重载 Problem H: 整型数组运算符重载 Tim ...
- C++学习26 运算符重载的概念和语法
所谓重载,就是赋予新的含义.函数重载(Function Overloading)可以让一个函数名有多种功能,在不同情况下进行不同的操作.运算符重载(Operator Overloading)也是一个道 ...
- C++运算符重载——重载特殊运算符
1.重载赋值运算符= 赋值运算符用于同类对象间的相互赋值.赋值运算符只能被重载为类的非静态成员函数,不能重载为友元函数和普通函数. 对于用户自定义的类而言,如果没有重载赋值运算符,那么C++编译器会为 ...
随机推荐
- GBDT笔记
GBDT笔记 GBDT是Boosting算法的一种,谈起提升算法我们熟悉的是Adaboost,它和AdaBoost算法不同: 区别如下: AdaBoost算法是利用前一轮的弱学习器的误差来更新样本权重 ...
- 基于laravel框架构建最小内容管理系统
校园失物招领平台开发 --基于laravel框架构建最小内容管理系统 摘要 针对目前大学校园人口密度大.人群活动频繁.师生学习生活等物品容易遗失的基本现状,在分析传统失物招领过程中的工作效率低下. ...
- ubuntu 快捷方式添加 applications添加
首先我们要了解,Ubuntu 的 Dash 里所有程序都是在 /usr/share/applications 中的,所以我们的思路很简单——建一个类似于“快捷方式”一样的东西扔进去就好了.所以第一步自 ...
- 怎样解决多层this指向全局对象window的问题
如下所示, 得到的结果里面, 第二个this指向的是window这个全局对象而非f2, 原因就是多层this造成的指向不明引起的. var a = { f1: function(){ console. ...
- 轻松搭建CAS 5.x系列(8)-在CAS Server增加双因素认证(DUO版)
概述说明 为了让系统更加安全,很多登录会加入双因素认证.何为双因素,如果把登陆作为开一扇门的话,那就是在原来的锁上再加一把锁,第二锁用新的钥匙,这样安全系数就更加高了. CAS是通过账号名和密码来认证 ...
- (三)Activiti之第一个程序以及Activiti插件的使用和Activiti表的解释
一.案例 1.1 建立Activiti Diagram图 new -> activiti ->Activiti Diagram,创建一个HelloWorld文件,后缀自动为bpmn,如下图 ...
- ASP.NET 使用 SyndicationFeed 输出 Rss
以前生成 RSS 都是使用拼接 Xml 的方式生成的,不仅麻烦而且还不规范. #region 输出指定分类编号的消息源内容... /// <summary> /// 输出指定分类编号的消息 ...
- Window环境下使用多个Git账号(github,gitee,gitlab,gogs等)
个人电脑之前已经设置好github账号了,公司用的是gitlab私服,一直互不干扰,因为用的是不同的电脑,也就懒得配置git多账户环境.最近看了一下多年空空如也的码云,想着怎么的也会用到gitee来托 ...
- Ajax中解析Json的两种方法
eval(); //此方法不推荐 JSON.parse(); //推荐方法 一.两种方法的区别 我们先初始化一个json格式的对象: var jsonDate = '{ "name" ...
- python实现tcp代理
1.代理流程图 2.实现代码 #! usr/bin/python2 import sys import socket import thread #handle local buffer def re ...