1,问题:

1,new 关键字创建出来的对象位于什么地方?

1,位于堆空间;

2,有没有可能位于其它地方?

1,有;

2,通过一些方式可以使动态创建的对象位于静态存储区;

3,这个存储区在程序结束后释放;

2,new/delete 被忽略的事实:

1,new/delete 的本质是 C++ 预定义的操作符;

1,new/delete 是关键字,但本质是预定义的操作符;

2,C++ 中操作符可以重载;

2,C++ 对这两个操作符做了严格的行为定义;

1,new:

1,获取足够大的内存空间(默认为堆空间);

2,在获取的空间中调用构造函数创建对象;

2,delete:

1,调用析构函数销毁对象;

2,归还对象所占用的空间(默认为堆空间);

3,在 C++ 中能够重载 new/delete 操作符:

1,全局重载(不推荐);

1,实际工程开发中不建议这样做

2,局部重载(针对具体类型进行重载);

1,针对具体的类重载;

3,重载 new/delete 的意义在于改变动态对象创建时的内存分配方式;

1,可以将创建的对象放到其它的内存空间里面去;

4,new/delete 的重载方式:

1,代码示例:

 // static member function
void* operator new(unsinged int size) // 第一步获取内存,参数表示需要获取的内存大小;
{
void* ret = NULL; /* ret point to allocated memory */ // 第二步在内存中调用构造函数创建对象; return ret;
} // static member function
void operator delete (void* p) // p 指针指向对应的对象地址,也就是要释放的地址;
{
/* free the memory which is pointed by p */
}

2,通过函数来对这两个操作符进行重载;

3,一般针对具体类来重载,所以说 new/delete 的重载函数就是类的成员函数,并且这两个重载函数默认为静态成员函数,写不写 static 都是静态成员函数;

3,静态存储区中创建动态对象编程实验:

 #include <iostream>
#include <string> using namespace std; class Test
{
static const unsigned int COUNT = ; static char c_buffer[]; // 本质是这里申请空间而下面只是标记使用而已;
static char c_map[]; int m_value;
public:
void* operator new (unsigned int size)
{
void* ret = NULL; // 如果这片内存已经满了,返回空; /* 查找在 c_buffer 里面那些位置是空闲的,可以用来创建 Test 对象 */
for(int i=; i<COUNT; i++)
{
if( !c_map[i] ) // 当前空间不可用了;
{
c_map[i] = ; // 标记为不可用; ret = c_buffer + i * sizeof(Test); // 查找 c_buffer 这片可用内存空间的首地址,并返回这片空间; cout << "succeed to allocate memory: " << ret << endl; break;
}
} return ret;
} void operator delete (void* p)
{
if( p != NULL ) // 空指针时候什么都不处理;
{
char* mem = reinterpret_cast<char*>(p);
int index = (mem - c_buffer) / sizeof(Test); // 得到要释放的动态对象在 c_map 中的位置;
int flag = (mem - c_buffer) % sizeof(Test); // 这些位置必须是固定的,如果 flag 不为 0,指针则不合法; if( (flag == ) && ( <= index) && (index < COUNT) )
{
c_map[index] = ; // 释放这个地址,即标记这个地址可用; cout << "succeed to free memory: " << p << endl;
}
}
}
}; char Test::c_buffer[sizeof(Test) * Test::COUNT] = {}; // 定义一块静态的内存空间,内存空间想要存储的是 Test 对象,最多存储 4 个 Test 对象;
char Test::c_map[Test::COUNT] = {}; // 标记数组,用于标记在那些位置已经创建了对象,作用是标记; int main(int argc, char *argv[])
{
cout << "===== Test Single Object =====" << endl; Test* pt = new Test; // 这里是在 c_buffer 里面的静态存储区当中的空间生成的; delete pt; cout << "===== Test Object Array =====" << endl; Test* pa[] = {}; for(int i=; i<; i++)
{
pa[i] = new Test; cout << "pa[" << i << "] = " << pa[i] << endl;
} for(int i=; i<; i++)
{
cout << "delete " << pa[i] << endl; delete pa[i];
} return ;
}

1,结论:

1,new/delete 关键字是可以重载的;

2,重载的意义是改变内存的分配方式,使得动态创建的对象不再位于堆空间里面;

3,这个实验位于自定义的静态存储区里面的 c_buffer 数组当中;

2,拓展:

1,工程中可以结合不同方法来应用 new/delete 特性;

2,将本实验的方法和二阶构造法结合在一起,我们就可以创建一个类,并且规定这个类最多产生多少个对象;

3,单例模式仅仅使得一个类只有一个对象存在,而这里的方法加上二阶构造就可以诞生 N 例模式;

4,问题:

1,如何在指定的地址上创建 C++ 对象?

1,我们已经掌握了在静态存储区里面创建对象,是否可以扩展下这个方法,在任意的地址上创建对象呢?

2,通过重载 new/delete 也许就可以在指定的地址上创建对象;

5,设计思路:

1,在类中重载 new/delete 操作符;

2,在 new 的操作符重载函数中返回指定的地址;

3,在 delete 操作符重载中标记对应的地址可用;

6,自定义动态对象的存储空间编程实验:

 #include <iostream>
#include <string>
#include <cstdlib> using namespace std; class Test
{
static unsigned int c_count; // 动态实时做决定,所以这个地方就不能有常量;
static char* c_buffer;
static char* c_map; int m_value;
public:
/* 动态指定想在什么类型上指定申请对象 */
static bool SetMemorySource(char* memory, unsigned int size)
{
bool ret = false; // 返回值为 bool 类型,告诉函数调用者,当前动态空间设置是否成功; c_count = size / sizeof(Test); // 计算传进来的空间可以创建多少对象; ret = (c_count && (c_map = reinterpret_cast<char*>(calloc(c_count, sizeof(char))))); if( ret ) // 空间至少为 1,且标记指针合法;
{
c_buffer = memory; // 将指定空间设置到 c_buffer 上;
}
else // 一切清零;
{
free(c_map); c_map = NULL;
c_buffer = NULL;
c_count = ;
} return ret;
} void* operator new (unsigned int size)
{
void* ret = NULL; /* 有指定的一个具体空间,通过各种计算和验证,看下所指定的空间上面是否可以动态创建对象,标准是 c_count 大于 0,此时意味着通过 setMemorySource() 所指定的空间是可以创建 Test 对象的,则走 if 路径,否则的话,走 else 路径,通过 malloc() 函数得到一片空间; */
if( c_count > ) //
{
for(int i=; i<c_count; i++)
{
if( !c_map[i] )
{
c_map[i] = ; ret = c_buffer + i * sizeof(Test); cout << "succeed to allocate memory: " << ret << endl; break;
}
}
}
else
{
ret = malloc(size); // 没有指定具体的在那个空间上生成对象时,通过 malloc 来申请默认的堆空间;
} return ret;
} void operator delete (void* p)
{
if( p != NULL )
{
if( c_count > )
{
char* mem = reinterpret_cast<char*>(p);
int index = (mem - c_buffer) / sizeof(Test);
int flag = (mem - c_buffer) % sizeof(Test); if( (flag == ) && ( <= index) && (index < c_count) )
{
c_map[index] = ; cout << "succeed to free memory: " << p << endl;
}
}
else
{
free(p); // 和上面对应
}
}
}
}; unsigned int Test::c_count = ;
char* Test::c_buffer = NULL;
char* Test::c_map = NULL; int main(int argc, char *argv[])
{
char buffer[] = {}; // 定义一片栈上空间,用于存放对象; Test::SetMemorySource(buffer, sizeof(buffer)); cout << "===== Test Single Object =====" << endl; Test* pt = new Test; delete pt; cout << "===== Test Object Array =====" << endl; Test* pa[] = {}; for(int i=; i<; i++) // 只有 3 个对象的空间,则后两个对象指向为 NULL;
{
pa[i] = new Test; cout << "pa[" << i << "] = " << pa[i] << endl;
} for(int i=; i<; i++)
{
cout << "delete " << pa[i] << endl; delete pa[i];
} return ;
}

1,通过重载 new/delete,我们可以在任意指定的位置动态创建 C++ 对象;

7,new[]/delete[] 与 new/delete 完全不同:

1,动态对象数组创建通过 new[] 完成;

2,动态对象数组的销毁通过 delete[] 完成;

3,new[]/delete[] 能够被重载,进而改变内存管理方式;

1,这是两个新的操作符;

8,new[]/delete[] 的重载方式:

1,代码示例:

 // static member function
void* operator new[] (unsigned int size)
{
rerurn malloc(size);
} // static member function
void operator delete[] (void* p)
{
free(p);
}

2,通过类的静态成员函数来重载,不写 static,这两个成员函数在类中也是      静态的;

9,注意事项:

1,nwe[] 实际需要返回的内存空间可能比期望的要多;

1,需要额外的空间来保存数组的信息;

2,如数组长度信息,因为编译器要自动的为我们调用构造函数和析构函数,不保存长度信息,编译器不知道要调用多少次构造函数和析构函数;

2,对象数组占用的内存中需要保存数组信息;

3,数组信息用于确定构造函数和析构函数的调用次数;

10,动态数组的内存管理编程实验:

 #include <iostream>
#include <string>
#include <cstdlib> using namespace std; class Test
{
int m_value;
public:
Test()
{
m_value = ;
} ~Test()
{
} void* operator new (unsigned int size)
{
cout << "operator new: " << size << endl; return malloc(size);
} void operator delete (void* p)
{
cout << "operator delete: " << p << endl; free(p);
} void* operator new[] (unsigned int size)
{
cout << "operator new[]: " << size << endl; return malloc(size);
} void operator delete[] (void* p)
{
cout << "operator delete[]: " << p << endl; free(p);
}
}; int main(int argc, char *argv[])
{
Test* pt = NULL; pt = new Test; // operator new: 4; delete pt; // operator delete: 0x8e5d008; pt = new Test[]; // operator new[]: 24;这里多了四个字节,用于保存数组的大小信息,因为编译器自动为我们自调用构造函数和析构函数; delete[] pt; // operator delete[]: 0x8e5d018; return ;
}

1,new/delete 和 new[]/delete[] 是完全不同的;

2,通过重载的方式说明了它们的不同;

3,意味着在实际的工程里面,有可能在 new 中函数的内存分配方式和 delete[] 中函数内存分配方式是不一样的,因此必须成对使用,必须要匹配;

4,假设 new[] 动态创建数组是从栈上分配的空间,然后 delete 想要将空间归还到堆空间去,如果交叉使用,则意味着有可能把栈上的空间归还到堆空间上,程序会崩溃,所以要成对出现,不要交叉使用,因为它们 new/delete 和 new[]/delete[] 完全不同;

11,小结:

1,new/delete 的本质为操作符;

2,可以通过全局函数重载 new/delete(不推荐);

3,可以针对具体的类重载new/delete;

4,new[]/delete[] 与 new/delete 完全不同;

C++中的自定义内存管理的更多相关文章

  1. C++解析(31):自定义内存管理(完)

    0.目录 1.遗失的关键字mutable 2.new / delete 3.new[] / delete[] 4.小结 5.C++语言学习总结 1.遗失的关键字mutable 笔试题: 统计对象中某个 ...

  2. iOS中引用计数内存管理机制分析

    在 iOS 中引用计数是内存的管理方式,虽然在 iOS5 版本中,已经支持了自动引用计数管理模式,但理解它的运行方式有助于我们了解程序的运行原理,有助于 debug 程序. 操作系统的内存管理分成堆和 ...

  3. NETTY4中的BYTEBUF 内存管理

    转 http://iteches.com/archives/65193 Netty4带来一个与众不同的特点是其ByteBuf的重现实现,老实说,java.nio.ByteBuf是我用得很不爽的一个AP ...

  4. delphi 自定义内存管理

    1.主要通过GetMemoryManager来hook原来的内存管理. 2.通过SetMemoryManager来设置你自己的新的内存管理,可以用一个内存池来优化和管理程序的内存调用情况. proce ...

  5. PHP中SESSION自定义会话管理器

    <?php class CustomSession implements SessionHandlerInterface{ private $link; private $lifetime; p ...

  6. 分析linux内核中的slub内存管理算法

    1. 分析的linux内核源码版本为4.18.0 2. 与slub相关的内核配置项为CONFIG_SLUB 3. 一切都从一个结构体数组kmalloc_caches开始,它的原型如下: ] __ro_ ...

  7. Netty4 中的内存管理

    在Netty4中引入了新的内存管理机制极大地提升其性能,本文将对该内在管理机制进行剖析. 这里有篇文章讲述了在推特(Twitter)内部 使用Netty的状况以及Netty4所带来的性能收益. 在分析 ...

  8. OC基础--内存管理中的@property关键字以及其参数

    在上一篇博客中整理的内存管理,管理类的代码量会感觉很大,而且如果对象多的话,感觉到代码有点冗余.下面就介绍Xcode中为我们自动生成内存管理代码的关键字@property 例如:在Person这个类中 ...

  9. iOS阶段学习第21天笔记(ARC内存管理-Copy-代理)

    iOS学习(OC语言)知识点整理 一.OC 中的ARC内存管理 1)ARC中释放对象的内存原则:看这个对象有没有强引用指向它 2)strong:强引用,默认情况下的引用都是强引用 3) weak:弱引 ...

随机推荐

  1. Android_(传感器)指南针

    Android方向传感器 传感器中的X:如上图所示,规定X正半轴为北,手机头部指向OF方向,此时X的值为0,如果手机头部指向OG方向,此时X值为90,指向OH方向,X值为180,指向OE,X值为270 ...

  2. pip & conda 换源

    conda换源方法具体参考清华大学镜像站Anaconda 镜像使用帮助 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn ...

  3. C++入门经典-例4.7-变量的作用域

    1:代码如下: // 4.7.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> using ...

  4. Docker入门-介绍和安装

    Docker容器 Docker是什么 Docker最初是dotCloud公司创建人Solomon Hykes在法国期间发起的一个公司内部项目,它是基于dotCloud公司多年云服务技术的一次革新,并于 ...

  5. What is the !! (not not) operator in JavaScript?

    What is the !! (not not) operator in JavaScript? 解答1 Coerces强制 oObject to boolean. If it was falsey ...

  6. redis学习之路

    cp /home/zzq/桌面/M+.txt /home/zzq/mySercet#将桌面文件下的M+.txt 复制到mySercetrm -rf M+.txt#强制递归删除M+.txt1.网络适配器 ...

  7. Java反序列化与远程代码执行

    https://mp.weixin.qq.com/s/asQIIF8NI_wvur0U0jNvGw 原创: feng 唯品会安全应急响应中心 2017-09-19 https://mp.weixin. ...

  8. ShuffleNet: An Extremely Efficient Convolutional Neural Network for Mobile Devices

    1. 摘要 作者介绍了一种计算非常高效的 CNN 结构称之为 ShuffleNet,它是专门为计算资源非常有限的移动设备设计的. 这种新的结构主要用到了两种操作:分组点卷积(pointwise gro ...

  9. react介绍、环境搭建、demo运行实例

    React官网:https://reactjs.org/docs/create-a-new-react-app.html cnpm网址:http://npm.taobao.org/ 1.react介绍 ...

  10. flutter tabbar创建与显示

    效果图 main.dart import 'package:flutter/material.dart'; import 'pages/index_page.dart'; void main() =& ...