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. Makefile样例

    Makefile1 src = $(wildcard ./*cpp) obj = $(patsubst %.cpp, %.o,$(src)) target = test $(target) : $(o ...

  2. 51nod-1640--天气晴朗的魔法(简单最小生成树)

    1640 天气晴朗的魔法 题目来源: 原创 基准时间限制:1 秒 空间限制:131072 KB 分值: 20 难度:3级算法题 这样阴沉的天气持续下去,我们不免担心起他的健康. 51nod魔法学校近日 ...

  3. python实战,

    1.把日志状态码为200得请求记录下来 记录信息(ip,访问时间,请求资源) 封装函数再次调用,健壮性try except #coding=utf-8import redef  aclog(path, ...

  4. Kotlin的高阶函数和常用高阶函数

    Kotlin的高阶函数和常用高阶函数 文章来源:企鹅号 - Android先生 高阶函数的定义 将函数当做参数或者是返回值的函数 什么是高阶函数 可以看看我们常用的 函数: 首先我们可以知道, 是 的 ...

  5. shiro.ini

    # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreeme ...

  6. 【转】diamond专题(二)– 核心原理介绍

    特别提示:本人博客部分有参考网络其他博客,但均是本人亲手编写过并验证通过.如发现博客有错误,请及时提出以免误导其他人,谢谢!欢迎转载,但记得标明文章出处:http://www.cnblogs.com/ ...

  7. 第七周总结&实验报告5

    这一周的课程内容比较难,而且比较不容易理解,所有学习的很吃力,现在接触的知识越来越多,也越来越难了,还是要多对照书本来进行学习! 这周主要学的有: 一.抽象类 1.Java中可以创建一种类专门用来当作 ...

  8. Linux Bash shell常用操作快捷键

    转自:https://linuxtoy.org/archives/bash-shortcuts.html 生活在 Bash shell 中,熟记以下快捷键,将极大的提高你的命令行操作效率. 编辑命令 ...

  9. MongoDB中的_id和ObjectId

    ObjectId是"_id"的默认类型.它设计成轻量型的,不同的机器都能用全局唯一的同种方法方便地生成它. 这是MongoDB采用ObjectId,而不是其他比较常规的做法(比如自 ...

  10. 使用JS生成HTML标签,以达到母板页的效果

    前台页面 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1. ...