运算符重载之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++编译器会为 ...
随机推荐
- sysbench安装和测试
1.首先安装依赖 yum install mariadb-devel yum install automake libtool –y 2.下载安装包 wget https://github.com/a ...
- redis的安装---Linux
1.下载https://redis.io/download wget http://download.redis.io/releases/redis-4.0.14.tar.gz tar -zxvf r ...
- java集合的作用
从架构的方面来理解,可能稍微容易一点.在编程中,需要管理很多对象集.比如某班全部同学,某个公司所有人员资料等.要管理这些资料,java必须提供某种数据结构支持.由于时间,空间,安全的考虑,有各种不同的 ...
- Python练习_函数进阶_day10
1. 1.作业 1,写函数,接收n个数字,求这些参数数字的和.(动态传参) 2,读代码,回答:代码中,打印出来的值a,b,c分别是什么?为什么? a=10 b=20 def test5(a,b): p ...
- 2、JDK8中的HashMap实现原理及源码分析
本篇提纲.png 本篇所述源码基于JDK1.8.0_121 在写上一篇线性表的文章的时候,笔者看的是Android源码中support24中的Java代码,当时发现这个ArrayList和Linked ...
- Trie-Tree
最近写了一些关于字典树的题目,这里做个简单的整理. 字典树,又叫单词查找树,顾名思义就是查单词的(不仅仅o),和词典一样.不同的是词典是用纸做的,而字典树是用树形结构构建的. 她用来快速检索你要的内容 ...
- 关于H5的一些相关基础知识
HTML只是简写全写是(Hyper Text Markup Language)表示的是:超文本标记语言; HTML5表示的是html的第5次重大修改的第5个版本,(而html5是W3C和WHATWG ...
- 【ASE模型组】Hint::neural 模型与case study
模型 基于搜索的提示系统 我们的系统用Pycee针对语法错误给出提示.然而,对于语法正确.结果错误的代码,我们需要另外的解决方式.因此,我们维护一些 (错误代码, 相应提示) 的数据,该数据可以由我们 ...
- InnoDB全文索引
### 如果想了解全文索引,可以直接将本文复制到mysql的新建查询中,依次执行,即可了解全文索引的相关内容及特性. -- InnoDB全文索引 -- 建表 CREATE TABLE fts_a ( ...
- NORDIC 关闭浮点运算FPU
1.打开FPU中断 NVIC_SetPriority(FPU_IRQn, APP_IRQ_PRIORITY_LOW);NVIC_EnableIRQ(FPU_IRQn); 2.中断中关闭FPU void ...