【C++ Primer | 19】控制内存分配

1. 测试代码: 

#include <iostream>
#include <new>
#include <cstring>
#include <cstdlib>
using namespace std;void* operator new(size_t size)
{cout << "global Override operator new" << endl;if (void* ptr = malloc(size))  return ptr;elsethrow bad_alloc();
}void* operator new(size_t size, int flag)
{cout << "global Override operator new: " << flag << endl;return (::operator new(size));
}void operator delete (void* ptr)
{cout << "global Override operator delete" << endl;free(ptr);ptr = nullptr;
}void operator delete (void* ptr, int flag)
{cout << "Override operator delete: " << flag << endl;::operator delete(ptr);ptr = nullptr;
}int main()
{int* ptr = new int(10);delete ptr;cout << endl << "------------" << endl << endl;ptr = new(20) int(10);delete ptr;return 0;
}

输出结果:

分析:

从上面的结果可以看出,new int(10);直接先调用 operator new(size_t size); 由于int没有构造函数,在那块内存上调用int的构造函数; 在delete ptr; 的时间直接调用 operator delete(void * ptr);这个函数

new(20) int(10);的时候,则调用重载版本的 operator new(size_t size, int flag); 而该函数有调用了 operator new(size_t size); 函数,释放的时候delete ptr;还是直接只调用operator delete(void * ptr);(注:这里初步提出为啥不调用operator delete(void * ptr, int flag); 这个函数来释放ptr ???因为它的用途不在这,而在于下面将要讲的。

 

2. 测试代码:

#include <iostream>
#include <new>
#include <cstring>
#include <cstdlib>
using namespace std;void* operator new(size_t size)
{cout << "global Override operator new" << endl;void* ptr = malloc(size);return ptr;
}void* operator new(size_t size, int flag)
{cout << "global Override operator new: " << flag << endl;return (::operator new(size));
}void operator delete (void* ptr)
{cout << "global Override operator delete" << endl;free(ptr);ptr = nullptr;
}void operator delete (void* ptr, int flag)
{cout << "Override operator delete: " << flag << endl;::operator delete(ptr);ptr = nullptr;
}class Base
{
public:Base(){cout << "Base construct" << endl;throw 2;}~Base(){cout << "Base destructor" << endl;}//类中定制的operator new会覆盖全局的函数,但可以通过简单的调用全局的函数来实现调用static void* operator new(size_t size){cout << "operator new of Base" << endl;return ::operator new(size); //调用全局的operator new}static void* operator new(size_t size, int flag){cout << "Override operator new of Base: " << flag << endl;return operator new(size);}static void operator delete(void* ptr){cout << "Operator delete of Base" << endl;::operator delete(ptr);}static void operator delete(void* ptr, int flag){cout << "Override operator delete of Base: " << flag << endl;operator delete(ptr);}int x;int y;
};
int main()
{try{Base* bptr = new(20) Base;}catch (...){cout << "catch a exception" << endl;}return 0;
}

输出结果:

 

2. 若是不给Base类重载 static void operator delete(void * ptr, int flag);这个函数,结果则如下图:

#include <iostream>
#include <new>
#include <cstring>
#include <cstdlib>
using namespace std;void* operator new(size_t size)
{cout << "global Override operator new" << endl;void* ptr = malloc(size);return ptr;
}void* operator new(size_t size, int flag)
{cout << "global Override operator new: " << flag << endl;return (::operator new(size));
}void operator delete (void* ptr)
{cout << "global Override operator delete" << endl;free(ptr);ptr = nullptr;
}void operator delete (void* ptr, int flag)
{cout << "Override operator delete: " << flag << endl;::operator delete(ptr);ptr = nullptr;
}class Base
{
public:Base(){cout << "Base construct" << endl;throw 2;}~Base(){cout << "Base destructor" << endl;}//类中定制的operator new会覆盖全局的函数,但可以通过简单的调用全局的函数来实现调用static void* operator new(size_t size){cout << "operator new of Base" << endl;return ::operator new(size); //调用全局的operator new}static void* operator new(size_t size, int flag){cout << "Override operator new of Base: " << flag << endl;return operator new(size);}static void operator delete(void* ptr){cout << "Operator delete of Base" << endl;::operator delete(ptr);}int x;int y;
};
int main()
{try{Base* bptr = new(20) Base;}catch (...){cout << "catch a exception" << endl;}return 0;
}

输出结果:

 

 

二、定位new表达式

operator new和operator delete和alloctor类的allocate和deallocate很像,都是负责分配和释放内存的函数,但是对于operator new分配的内存空间我们无法使用construct函数构造对象,我们应该使用new的定位new形式构造对象。

1. 测试代码:

#include <iostream>
#include <new>
using namespace std;const int chunk = 16;
class Foo
{
public:int val() { return _val; }Foo() { _val = 0; }
private:int _val;
};//预分配内存,但没有Foo对象
char* buf = new char[sizeof(Foo) * chunk];
int main(void)
{//在buf中创建一个Foo对象Foo* pb = new (buf) Foo;//检查一个对象是否被放在buf中if (pb->val() == 0){cout << "new expressio worked!" << endl;}//到这里不能再使用pbdelete[] buf;return 0;
}

placement new的作用就是:创建对象但是不分配内存,而是在已有的内存块上面创建对象。用于需要反复 创建并删除的对象上,可以降低分配释放内存的性能消耗。定位new表达式(placement new expression),允许程序员将对象创建在已经被分配好的内存中,new表的式的形式如下:

new (place_address) type
new (palce_address) type (initializer-list)

【Note】: 

  • 当只传入一个指针类型的实参时,定位new表达式构造对象但是不分配内存,这个指针没有要求,甚至可能是一个不是一个指向动态内存的指针
  • 调用析构函数会销毁对象,但是不会释放内存。

 

2. 测试代码:

#include <iostream>
using namespace std;
char addr1[100];
int main()
{cout << "******定位new表达式演示***by David***" << endl;char addr2[100];char *addr3 = new char[100];cout << "addr1 = " << (void*)addr1 << endl;cout << "addr2 = " << (void*)addr2 << endl;cout << "addr3 = " << (void*)addr3 << endl;int *p = nullptr;p = new(addr1)int;  把内存分配到静态区*p = 1;cout << (void*)p << "  " << *p << endl;p = new(addr2)int; 把内存分配到栈区*p = 2;cout << (void*)p << "  " << *p << endl;p = new(addr3)int;  //把内存分配到堆区*p = 3;cout << (void*)p << "  " << *p << endl;return 0;
}

输出结果:

 

参考资料

  • 定制自己的new和delete:operator new 和 operator delete
  • 特殊的工具和技术

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/385372.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【第15章】虚函数

一、为什么基类中的析构函数要声明为虚析构函数&#xff1f; 直接的讲&#xff0c;C中基类采用virtual虚析构函数是为了防止内存泄漏。具体地说&#xff0c;如果派生类中申请了内存空间&#xff0c;并在其析构函数中对这些内存空间进行释放。假设基类中采用的是非虚析构函数&am…

【C++ Primer | 08】IO库

一、istringstream类 描述&#xff1a;从流中提取数据&#xff0c;支持 >> 操作 这里字符串可以包括多个单词&#xff0c;单词之间使用空格分开 #include <iostream> #include <sstream> using namespace std; int main() {istringstream istr(&quo…

EXEC函数族的一般规律

事实上&#xff0c;只有execve是真正的系统调用&#xff0c;其它五个函数最终都调用execve&#xff0c;所以execve在man手册第2节&#xff0c;其它函数在man手册第3节。这些函数之间的关系如下图所示。

进程间通信的方法

Linux环境下&#xff0c;进程地址空间相互独立&#xff0c;每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到&#xff0c;所以进程和进程之间不能相互访问&#xff0c;要交换数据必须通过内核&#xff0c;在内核中开辟一块缓冲区&#xff0c;进…

pipe函数

#include <unistd.h> int pipe(int pipefd[2]); 作用&#xff1a;创建管道 成功&#xff1a;0&#xff1b;失败&#xff1a;-1&#xff0c;设置errno 函数调用成功返回r/w两个文件描述符。无需open&#xff0c;但需手动close。规定&#xff1a;fd[0] …

mmap内存映射、system V共享内存和Posix共享内存

linux内核支持多种共享内存方式&#xff0c;如mmap内存映射&#xff0c;Posix共享内存&#xff0c;以system V共享内存。当内核空间和用户空间存在大量数据交互时&#xff0c;共享内存映射就成了这种情况下的不二选择。它能够最大限度的降低内核空间和用户空间之间的数据拷贝&a…

匿名映射

通过使用我们发现&#xff0c;使用映射区来完成文件读写操作十分方便&#xff0c;父子进程间通信也较容易。但缺陷是&#xff0c;每次创建映射区一定要依赖一个文件才能实现。通常为了建立映射区要open一个temp文件&#xff0c;创建好了再unlink、close掉&#xff0c;比较麻烦。…

信号的产生和状态

信号的产生&#xff1a;1.按键产生&#xff0c;如&#xff1a;Ctrlc&#xff08;内核向进程发送信号&#xff0c;杀死该进程&#xff09;、Ctrlz、Ctrl\&#xff1b;2.系统调用产生&#xff0c;如&#xff1a;kill、raise、abort&#xff1b;3.软件条件产生&#xff0c;如&…

【C++ Priemr | 15】虚函数常见问题

1. 在成员函数中调用虚函数&#xff1a; #include <iostream> using namespace std; class CBase { public:void func1(){func2();}virtual void func2() { cout << "CBase::func2()" << endl; } }; class CDerived : public CBase { public:virt…

965. 单值二叉树

如果二叉树每个节点都具有相同的值&#xff0c;那么该二叉树就是单值二叉树。 只有给定的树是单值二叉树时&#xff0c;才返回 true&#xff1b;否则返回 false。 示例 1&#xff1a; 输入&#xff1a;[1,1,1,1,1,null,1] 输出&#xff1a;true示例 2&#xff1a; 输入&#…

958. 二叉树的完全性检验

给定一个二叉树&#xff0c;确定它是否是一个完全二叉树。 百度百科中对完全二叉树的定义如下&#xff1a; 若设二叉树的深度为 h&#xff0c;除第 h 层外&#xff0c;其它各层 (1&#xff5e;h-1) 的结点数都达到最大个数&#xff0c;第 h 层所有的结点都连续集中在最左边&a…

信号捕捉(signal、sigaction)

信号的基本属性&#xff1a;软中断&#xff0c;由内核发送&#xff0c;内核处理。某个进程通过内核向另一个进程发送信号时&#xff08;引起信号产生的五个因素&#xff09;&#xff0c;另一个进程将会陷入内核进行中断处理&#xff0c;未决信号集中相应信号置1&#xff0c;当递…

可/不可重入函数

一个函数在被调用执行期间&#xff08;尚未调用结束&#xff09;&#xff0c;由于某种时序&#xff08;递归或者处理信号捕捉时等情况&#xff09;又被重复调用&#xff0c;称之为“重入”。根据函数实现的方法可分为“可重入函数”和“不可重入函数”两种。看如下程序。 可以看…

终端的启动流程

在Linux操作系统启动时&#xff0c;首先加载的进程就是init进程&#xff08;ID为1&#xff09;&#xff0c;其余进程都是init进程产生的&#xff08;fork&#xff0c;然后exec金蝉脱壳&#xff09;&#xff0c;因此系统中所有进程都可以看成是init进程的子孙进程。可以通过ps a…

网络终端

虚拟终端或串口终端的数目是有限的&#xff0c;虚拟终端&#xff08;字符控制终端&#xff09;一般就是/dev/tty1∼/dev/tty6六个&#xff0c;串口终端的数目也不超过串口的数目。然而网络终端或图形终端窗口的数目却是不受限制的&#xff0c;这是通过伪终端&#xff08;Pseudo…

线程的概念

线程&#xff08;LWP&#xff0c;light weight process&#xff09;是轻量级的进程&#xff0c;本质仍是进程&#xff08;在类unix环境下&#xff09;。进程有独立地址空间&#xff0c;拥有PCB&#xff1b;线程也有PCB&#xff0c;但没有独立的地址空间&#xff08;共享&#x…

C指针深度解析

&#xff08;1&#xff09;指针的概念 指针是一种数据类型&#xff0c;而内存地址是这种数据类型具体的值&#xff08;注意区分两者的概念&#xff09;。先说一下什么是内存地址&#xff1a;假设CPU的寻址方式是以字节寻址的&#xff0c;即每一个字节对应一个地址编号&#xf…

C++设计模式之策略模式(Strategy)

Strategy策略模式作用&#xff1a;定义了算法家族&#xff0c;分别封装起来&#xff0c;让他们之间可以互相替换&#xff0c;此模式让算法的变化&#xff0c;不会影响到使用算法的客户。 UML图&#xff1a; 代码实现 #include <iostream> using namespace std;class St…

【C++ Primer | 15】面试问题

在成员函数中调用虚函数 #include <iostream> using namespace std; class CBase { public:void func1(){func2();}virtual void func2() {cout << "CBase::func2()" << endl;} }; class CDerived:public CBase { public:virtual void func2() {…

STL源码剖析面试问题

当vector的内存用完了&#xff0c;它是如何动态扩展内存的&#xff1f;它是怎么释放内存的&#xff1f;用clear可以释放掉内存吗&#xff1f;是不是线程安全的&#xff1f; vector内存用完了&#xff0c;会以当前size大小重新申请2* size的内存&#xff0c;然后把原来的元素复制…