记录学习的点点滴滴,参考侯捷<<C++内存管理>>

我们先重载一下C++的几个内存管理函数 operator new, operator new[], operator delete, operator delete[]

1.创建一个类

class Foo
{
private:
int _id; //
long _data;//
string _str;// public:
Foo():_id()
{
cout << "default ctor.this=" << this << " id=" << _id << endl;
}
Foo(int a):_id(a)
{
cout << "ctor.this=" << this << " id=" << _id << endl;
} // virtual
~Foo()
{
cout << "dtor.this=" << this << " id=" << _id << endl;
} //申请内存的函数必须是必须是静态的 调用这个函数时一般都是正在创建这个对象 所以当调用时你这个对象还不存在 所以需要声明成静态
static void* operator new(size_t size);
static void operator delete(void* pdead, size_t size);
static void* operator new[](size_t size);
static void operator delete[](void* pdead, size_t size);
};

上一节提到过operator new 和 operator delete的实现是基于malloc()和free() 这边也直接在函数内声明这两个函数

    void* Foo::operator new(size_t size)//size 为将要申请的内存大小
{
Foo* p = (Foo*)malloc(size);
cout <<"operator new().sizeof()="<< size << " return=" << p <<endl;
return p; //p 为内存起始点
} void Foo::operator delete(void* pdead, size_t size)//pdead 删除点 和上面的p为同一个位置 size 为将要删除的内存大小
{
cout <<"operator delete.pdead=" << pdead << " size=" << size <<endl;
cout << endl;
free(pdead);
} void* Foo::operator new[](size_t size)
{
Foo* p = (Foo*)malloc(size);
cout <<"operator new[].size="<< size <<" return=" << p << endl;
return p;
} void Foo::operator delete[](void* pdead, size_t size)
{
cout<< "operator delete[].pdead=" << pdead << " size="<< size <<endl;
cout << endl;
free(pdead);
}

下面调用一下测试函数

    void test()
{
cout << "sizeof(Foo)="<<sizeof(Foo) << endl;
Foo* p = new Foo();
delete p; Foo* pArray = new Foo[];
delete [] pArray;
}

下图示侯捷在<<C++内存管理>> 在VC6上的内存管理图片

class A
{
int a;
...
int j;//共10个int型变量
}

那么这个类的Debug版实际内存应该是下图这种

这个图片分为 白色的上下cookie 上下cookie占8字节 10个灰色的data数据占40字节 12字节的补全字节 和一个橙色的4字节空位,

上面我用的是Qt的编译器

看上面log的第7行 "operator new[].size=64  return = 0x8714c0" 起始内存点在0x8714c0.但是构造函数的内存起点是0x8714c4

那么这4个字节就是上图白色部分的cookie,它的内存分配应该是下图所示

那么当我们每创建一组这样的数据,那就会有上下两个cookie(8字节),那么当我们创建多组数据的话就会有很多组cookie,那么就会占用一些花销

有一种方法减少这种内存的花销,那就是内存池操作


内存池

先说一下内存池的优点

1.减少malloc的使用,提高运行效率

2.可以指定对齐方式

3.减少内存碎片,减少cookie

创建一个Screen类

class Screen
{
public:
Screen(int x): i(x)
{
//todo
} int get()
{
return i;
} void* operator new(size_t);
void operator delete(void*, size_t); private:
Screen* next;//4bit
static Screen* freeStore;
static const int screenChunk;//想要创建多少组 private:
int i; //4bit
}; Screen* Screen::freeStore = ;
const int Screen::screenChunk = ;

重载operator new(创建内存池)和operator delete

void* Screen::operator new(size_t size)
{
Screen* p;
if(!freeStore)
{
//linked list是空的,所以申请一大块内存
size_t chunk = screenChunk * size; //192 Screen的内存大小为8共24组 24 * 8 = 192
freeStore = p = reinterpret_cast<Screen*>(new char[chunk]);
cout << "startPisotion: " << p << endl; //将一大块内存分割成片段,当做linked list串接起来
for(; p != &freeStore[screenChunk-]; ++p)
{
p->next = p+;
}
p->next = ;
}
p = freeStore;
freeStore = freeStore->next; return p;
} void Screen::operator delete(void* p, size_t)
{
//将delete object插回 free list前端
(static_cast<Screen*>(p)) -> next = freeStore;
freeStore = static_cast<Screen*>(p);
}

测试代码

void test()
{
cout << sizeof(Screen) << endl; size_t const N = ; Screen* p[N]; cout << "overload operator new" << endl;
for(int i=; i<N; i++)
{
p[i] = new Screen(i);
} for(int i = ; i<; i++)
{
cout << p[i] << endl;//输出每个Screen的内存起点
} for(int i=; i<N; i++)
{
delete p[i];
} cout << "glob operator new" << endl; Screen* q[N]; for(int i=; i<N; i++)
{
q[i] = ::new Screen(i);
} for(int i = ; i<; i++)
{
cout << q[i] << endl;
} for(int i=; i<N; i++)
{
::delete q[i];
}
}

测试结果

你会发现调用全局的和调用重载的函数结果差别会很大

全局operator new的默认对齐方式是16字节对齐而且会发现没有了cookie,而且减少了malloc得使用提高使用效率

测试代码

namespace wzj03_class12_15 {

class Screen
{
public:
Screen(int x): i(x)
{
//todo
} int get()
{
return i;
} void* operator new(size_t);
void operator delete(void*, size_t); private:
Screen* next;//4bit
static Screen* freeStore;
static const int screenChunk; private:
int i; //4bit
}; Screen* Screen::freeStore = ;
const int Screen::screenChunk = ; void* Screen::operator new(size_t size)
{
Screen* p;
if(!freeStore)
{
//linked list是空的,所以申请一大块内存
size_t chunk = screenChunk * size; //192 Screen的内存大小为8共24组 24 * 8 = 192
freeStore = p = reinterpret_cast<Screen*>(new char[chunk]);
cout << "startPisotion: " << p << endl; //将一大块内存分割成片段,当做linked list串接起来
for(; p != &freeStore[screenChunk-]; ++p)
{
p->next = p+;
}
p->next = ;
}
p = freeStore;
freeStore = freeStore->next; return p;
} void Screen::operator delete(void* p, size_t)
{
//将delete object插回 free list前端
(static_cast<Screen*>(p)) -> next = freeStore;
freeStore = static_cast<Screen*>(p);
} void test()
{
cout << sizeof(Screen) << endl; size_t const N = ; Screen* p[N]; cout << "overload operator new" << endl;
for(int i=; i<N; i++)
{
p[i] = new Screen(i);
} for(int i = ; i<; i++)
{
cout << p[i] << endl;
} for(int i=; i<N; i++)
{
delete p[i];
} cout << "glob operator new" << endl; Screen* q[N]; for(int i=; i<N; i++)
{
q[i] = new Screen(i);
} for(int i = ; i<; i++)
{
cout << q[i] << endl;
} for(int i=; i<N; i++)
{
delete q[i];
}
} }

这段代码是封装了一个小型内存池

namespace wzj03_class12_15_1 {
class myAllocator
{
private:
struct obj
{
struct obj* next;
}; public:
void* allocate(size_t);
void deallocate(void*, size_t);
private:
obj* freeStore = nullptr;
const int CHUNK = ;
}; void myAllocator::deallocate(void* p, size_t size)
{
cout << "myAllocator::deallocate" << "size: " << size <<endl;
((obj*)p)->next = freeStore;
freeStore = (obj*)p;
} void* myAllocator::allocate(size_t size)
{
cout << "myAllocator::allocate" << "size: " << size <<endl;
obj* p;
if(!freeStore)
{
size_t chunk = CHUNK * size;
freeStore = p = (obj*)malloc(chunk); for(int i=; i<(CHUNK-); ++i)
{
p->next = (obj*)((char*)p + size);
p = p->next;
} p->next = nullptr;
}
p= freeStore;
freeStore = freeStore -> next; return p;
} class Foo
{
public:
long L;
string str;
static myAllocator myAlloc;
public:
Foo(long l): L(l)
{
//todo
} static void* operator new(size_t size)
{
return myAlloc.allocate(size);
} static void operator delete(void* pdead, size_t size)
{
return myAlloc.deallocate(pdead, size);
}
};
myAllocator Foo::myAlloc; void test()
{
cout << sizeof(Foo) << endl; size_t const N = ; Foo* p[N]; cout << "overload operator new" << endl;
for(int i=; i<N; i++)
{
p[i] = new Foo(i);
} for(int i = ; i<N; i++)
{
cout << p[i] << endl;
} for(int i=; i<N; i++)
{
delete p[i];
} cout << "glob operator new" << endl; Foo* q[N]; for(int i=; i<N; i++)
{
q[i] = ::new Foo(i);
} for(int i = ; i<N; i++)
{
cout << q[i] << endl;
} for(int i=; i<N; i++)
{
::delete q[i];
}
} }

C++内存管理-重载内存管理函数的更多相关文章

  1. (转)从内存管 理、内存泄漏、内存回收探讨C++内存管理

    http://www.cr173.com/html/18898_all.html 内存管理是C++最令人切齿痛恨的问题,也是C++最有争议的问题,C++高手从中获得了更好的性能,更大的自由,C++菜鸟 ...

  2. davlik虚拟机内存管理之一——内存分配

    转载自http://www.miui.com/thread-74715-1-1.html dalvik虚拟机是Google在Android平台上的Java虚拟机的实现,内存管理是dalvik虚拟机中的 ...

  3. C++ Primer : 第十二章 : 动态内存之动态内存管理(new和delete)

    C++语言定义了两个运算符来分配和释放动态内存:运算符new分配内存,运算符delete释放new分配的内存. 运算符new和delete 使用new动态分配和初始化对象 在自由空间分配的内存是无名的 ...

  4. Delphi的内存管理及内存泄露问题 FastMM4

    这几天因为一个程序长时间运行出现比较严重的内存泄露问题,开始关注了一下内存管理方面的东西,以前也注意内存管理,创建了对象及时释放,但总有忘了处理的情况. 在Delphi中没有自动回收机制,所以一定要及 ...

  5. Java的内存管理与内存泄露

    作为Internet最流行的编程语言之一,Java现正非常流行.我们的网络应用程序就主要采用Java语言开发,大体上分为客户端.服务器和数据库三个层次.在进入测试过程中,我们发现有一个程序模块系统内存 ...

  6. android 管理Bitmap内存 - 开发文档翻译

    由于本人英文能力实在有限,不足之初敬请谅解 本博客只要没有注明“转”,那么均为原创,转贴请注明本博客链接链接   Managing Bitmap Memory 管理Bitmap内存 In additi ...

  7. [内存管理]连续内存分配器(CMA)概述

    作者:Younger Liu, 本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 未本地化版本许可协议进行许可. 原文地址:http://lwn.net/Articles/396657/ 1 ...

  8. C语言(记录)——内存相关_2:内存的编址与管理

    本文是基于嵌入式的C语言 --------------------------------------------------------------------------------------- ...

  9. kmalloc分配物理内存与高端内存映射--Linux内存管理(十八)

    1 前景回顾 1.1 内核映射区 尽管vmalloc函数族可用于从高端内存域向内核映射页帧(这些在内核空间中通常是无法直接看到的), 但这并不是这些函数的实际用途. 重要的是强调以下事实 : 内核提供 ...

随机推荐

  1. BZOJ1632: [Usaco2007 Feb]Lilypad Pond SPFA+最短路计数

    Description 为了让奶牛们娱乐和锻炼,农夫约翰建造了一个美丽的池塘.这个长方形的池子被分成了M行N列个方格(1≤M,N≤30).一些格子是坚固得令人惊讶的莲花,还有一些格子是岩石,其余的只是 ...

  2. 【Android实验】第一个Android程序与Activity生命周期

    目录 第一个Android程序和Activity生命周期 实验目的 实验要求 实验过程 1. 程序正常启动与关闭 2. 外来电话接入的情况 3. 外来短信接入的情况 4. 程序运行中切换到其他程序(比 ...

  3. ACMG遗传变异分类标准与指南

    2015年,美国权威机构——美国医学遗传学与基因组学学会(ACMG)编写和发布了<ACMG遗传变异分类标准与指南>.为帮助我国医疗工作者和遗传咨询从业者更好地理解ACMG遗传变异分类标准. ...

  4. Linux——系统引导流程学习简单笔记

    开启电源: 固件 firmware(CMOS/BIOS) → POST 加电自检 对硬件就行检查 ↓ 自举程序 BootLoader(GRUB) → 载入内核 ↓ 载入内核 Kernel 1:驱动硬件 ...

  5. ubuntu14.04(server amd64)免密码sudo

    vi /etc/sudoers.d/nopasswd4sudo 加入以下内容 用户名 ALL=(ALL) NOPASSWD : ALL

  6. HDU 1540 Tunnel Warfare

    HDU 1540 思路1: 树状数组+二分 代码: #include<bits/stdc++.h> using namespace std; #define ll long long #d ...

  7. C# 中的集合(Array/ArrayList/List<T>/HashTable/Dictionary)

    int [] numbers = new int[5]; // 长度为5,元素类型为 int. string[,] names = new string[5,4]; // 5*4 的二维数组 byte ...

  8. English trip -- VC(情景课)1 A Get ready

    Meet your classmates 见见你的同学 Look at the picture. What do you see? 看图片.你看到了什么? calendar  日历 bookcase ...

  9. 20170727xlVBA根据数据表和模板工作簿生成个人明细表工作簿

    Sub CreateTables() Dim Wb As Workbook Dim OpenWb As Workbook Dim Sht As Worksheet Dim Rng As Range D ...

  10. codeforces 576c// Points on Plane// Codeforces Round #319(Div. 1)

    题意:有n个点,找到一个顺序走遍这n个点,并且曼哈顿距离不超过25e8. 由于给的点坐标都在0-1e6之间.将x轴分成1000*1000,即1000长度为1块.将落在同一块的按y排序,编号为奇的块和偶 ...