0.目录

1.遗失的关键字mutable

2.new / delete

3.new[] / delete[]

4.小结

5.C++语言学习总结

1.遗失的关键字mutable

笔试题:

统计对象中某个成员变量的访问次数

遗失的关键字:

  • mutable是为了突破const函数的限制而设计的
  • mutable成员变量将永远处于可改变的状态
  • mutable在实际的项目开发中被严禁滥用

mutable的深入分析:

  • mutable成员变量破坏了只读对象的内部状态
  • const成员函数保证只读对象的状态不变性
  • mutable成员变量的出现无法保证状态不变性

示例1——使用mutable实现成员变量的访问统计:

#include <iostream>

using namespace std;

class Test
{
int m_value;
mutable int m_count;
public:
Test(int value = 0)
{
m_value = value;
m_count = 0;
} int getValue() const
{
m_count++; return m_value;
} void setValue(int value)
{
m_count++;
m_value = value;
} int getCount() const
{
return m_count;
}
}; int main(int argc, char *argv[])
{
Test t; t.setValue(100); // 满足普通对象 cout << "t.m_value = " << t.getValue() << endl;
cout << "t.m_count = " << t.getCount() << endl; const Test ct(200); // 满足只读对象 cout << "ct.m_value = " << ct.getValue() << endl;
cout << "ct.m_count = " << ct.getCount() << endl; return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
t.m_value = 100
t.m_count = 2
ct.m_value = 200
ct.m_count = 1

示例2——不使用mutable实现成员变量的访问统计:

#include <iostream>

using namespace std;

class Test
{
int m_value;
int * const m_pCount;
/* mutable int m_count; */
public:
Test(int value = 0) : m_pCount(new int(0))
{
m_value = value;
/* m_count = 0; */
} int getValue() const
{
/* m_count++; */
*m_pCount = *m_pCount + 1;
return m_value;
} void setValue(int value)
{
/* m_count++; */
*m_pCount = *m_pCount + 1;
m_value = value;
} int getCount() const
{
/* return m_count; */
return *m_pCount;
} ~Test()
{
delete m_pCount;
}
}; int main(int argc, char *argv[])
{
Test t; t.setValue(100); // 满足普通对象 cout << "t.m_value = " << t.getValue() << endl;
cout << "t.m_count = " << t.getCount() << endl; const Test ct(200); // 满足只读对象 cout << "ct.m_value = " << ct.getValue() << endl;
cout << "ct.m_count = " << ct.getCount() << endl; return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
t.m_value = 100
t.m_count = 2
ct.m_value = 200
ct.m_count = 1

2.new / delete

面试题:

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

被忽略的事实:

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

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

  • new:

    1. 获取足够大的内存空间(默认为堆空间)
    2. 在获取的空间中调用构造函数创建对象
  • delete:
    1. 调用析构函数销毁对象
    2. 归还对象所占用的空间(默认为堆空间)

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

  • 全局重载(不推荐)
  • 局部重载(针对具体类进行重载)

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

new / delete 的重载方式:



(new和delete默认就是静态成员函数,不管你写不写static。)

示例——静态存储区中创建动态对象:

#include <iostream>

using namespace std;

class Test
{
static const unsigned int COUNT = 4;
static char c_buffer[];
static char c_map[]; int m_value;
public:
void* operator new (unsigned long size)
{
void* ret = NULL; for(int i=0; i<COUNT; i++)
{
if( !c_map[i] )
{
c_map[i] = 1; ret = c_buffer + i * sizeof(Test); 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);
int flag = (mem - c_buffer) % sizeof(Test); if( (flag == 0) && (0 <= index) && (index < COUNT) )
{
c_map[index] = 0; cout << "succeed to free memory: " << p << endl;
}
}
}
}; char Test::c_buffer[sizeof(Test) * Test::COUNT] = {0};
char Test::c_map[Test::COUNT] = {0}; int main(int argc, char *argv[])
{
Test* pt = new Test; delete pt; return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
succeed to allocate memory: 0x601380
succeed to free memory: 0x601380

(可以看到new和delete的重载函数确实被调用了。)

示例——进一步试验:

int main(int argc, char *argv[])
{
cout << "===== Test Single Object =====" << endl; Test* pt = new Test; delete pt; cout << "===== Test Object Array =====" << endl; Test* pa[5] = {0}; for(int i=0; i<5; i++)
{
pa[i] = new Test; cout << "pa[" << i << "] = " << pa[i] << endl;
} for(int i=0; i<5; i++)
{
cout << "delete " << pa[i] << endl; delete pa[i];
} return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
===== Test Single Object =====
succeed to allocate memory: 0x6013a0
succeed to free memory: 0x6013a0
===== Test Object Array =====
succeed to allocate memory: 0x6013a0
pa[0] = 0x6013a0
succeed to allocate memory: 0x6013a4
pa[1] = 0x6013a4
succeed to allocate memory: 0x6013a8
pa[2] = 0x6013a8
succeed to allocate memory: 0x6013ac
pa[3] = 0x6013ac
pa[4] = 0
delete 0x6013a0
succeed to free memory: 0x6013a0
delete 0x6013a4
succeed to free memory: 0x6013a4
delete 0x6013a8
succeed to free memory: 0x6013a8
delete 0x6013ac
succeed to free memory: 0x6013ac
delete 0

(因为c_buffer的空间只能创建4个对象,因此for循环中的第5个对象就没有空间了,于是返回了空指针。)

(可以使用这个方法加上二阶构造模式就可以将单例模式推广到多例模式!)

面试题:

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

解决方案:

  • 在类中重载new / delete操作符
  • 在new的操作符重载函数中返回指定的地址
  • 在delete操作符重载中标记对应的地址可用

示例——自定义动态对象的存储空间:

#include <iostream>
#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; c_count = size / sizeof(Test); ret = (c_count && (c_map = reinterpret_cast<char*>(calloc(c_count, sizeof(char))))); if( ret )
{
c_buffer = memory;
}
else
{
free(c_map); c_map = NULL;
c_buffer = NULL;
c_count = 0;
} return ret;
} void* operator new (unsigned long size)
{
void* ret = NULL; if( c_count > 0 )
{
for(int i=0; i<c_count; i++)
{
if( !c_map[i] )
{
c_map[i] = 1; ret = c_buffer + i * sizeof(Test); cout << "succeed to allocate memory: " << ret << endl; break;
}
}
}
else
{
ret = malloc(size);
} return ret;
} void operator delete (void* p)
{
if( p != NULL )
{
if( c_count > 0 )
{
char* mem = reinterpret_cast<char*>(p);
int index = (mem - c_buffer) / sizeof(Test);
int flag = (mem - c_buffer) % sizeof(Test); if( (flag == 0) && (0 <= index) && (index < c_count) )
{
c_map[index] = 0; cout << "succeed to free memory: " << p << endl;
}
}
else
{
free(p);
}
}
}
}; unsigned int Test::c_count = 0;
char* Test::c_buffer = NULL;
char* Test::c_map = NULL; int main(int argc, char *argv[])
{
char buffer[12] = {0}; Test::SetMemorySource(buffer, sizeof(buffer)); cout << "===== Test Single Object =====" << endl; Test* pt = new Test; delete pt; cout << "===== Test Object Array =====" << endl; Test* pa[5] = {0}; for(int i=0; i<5; i++)
{
pa[i] = new Test; cout << "pa[" << i << "] = " << pa[i] << endl;
} for(int i=0; i<5; i++)
{
cout << "delete " << pa[i] << endl; delete pa[i];
} return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
===== Test Single Object =====
succeed to allocate memory: 0x7ffc6b2823d0
succeed to free memory: 0x7ffc6b2823d0
===== Test Object Array =====
succeed to allocate memory: 0x7ffc6b2823d0
pa[0] = 0x7ffc6b2823d0
succeed to allocate memory: 0x7ffc6b2823d4
pa[1] = 0x7ffc6b2823d4
succeed to allocate memory: 0x7ffc6b2823d8
pa[2] = 0x7ffc6b2823d8
pa[3] = 0
pa[4] = 0
delete 0x7ffc6b2823d0
succeed to free memory: 0x7ffc6b2823d0
delete 0x7ffc6b2823d4
succeed to free memory: 0x7ffc6b2823d4
delete 0x7ffc6b2823d8
succeed to free memory: 0x7ffc6b2823d8
delete 0
delete 0

3.new[] / delete[]

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

  • 动态对象数组创建通过 new[] 完成
  • 动态对象数组的销毁通过 delete[] 完成
  • new[] / delete[] 能够被重载,进而改变内存管理方式

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

注意事项:

  • new[]实际需要返回的内存空间可能比期望的要多
  • 对象数组占用的内存中需要保存数组信息
  • 数组信息用于确定构造函数析构函数的调用次数

示例——动态数组的内存管理:

#include <iostream>
#include <cstdlib> using namespace std; class Test
{
int m_value;
public:
Test()
{
m_value = 0;
} ~Test() { } void* operator new (unsigned long 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 long 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; delete pt; pt = new Test[5]; delete[] pt; return 0;
}

运行结果为:

[root@bogon Desktop]# g++ test.cpp
[root@bogon Desktop]# ./a.out
operator new: 4
operator delete: 0x150a010
operator new[]: 28
operator delete[]: 0x150a030

4.小结

  • new / delete 的本质为操作符
  • 可以通过全局函数重载 new / delete (不推荐)
  • 可以针对具体的类重载 new / delete
  • new[] / delete[] 与 new / delete 完全不同
  • new[] / delete[] 也是可以被重载的操作符
  • new[] 返回的内存空间可能比期望的要多

5.C++语言学习总结

本系列学习的是“经典”C++语言:

“经典”指的是什么?

  • C++ 98/03 标准在实际工程中的常用特性
  • 大多数企业的产品开发中需要使用的C++技能

C++语言的学习需要重点在于以下几个方面:

  • C语言到C++的改进有哪些?
  • 面向对象的核心是什么?
  • 操作符重载的本质是什么?
  • 模板的核心意义是什么?
  • 异常处理的使用方式是什么?

C++解析(31):自定义内存管理(完)的更多相关文章

  1. 31 (OC)* 内存管理

    31 (OC)  内存管理 一:内存管理黄金法则. 如果对一个对象使用了alloc.[Mutable]copy,retain,那么你必须使用相应的realease或者autorelease 二:内存管 ...

  2. OpenHarmony3.1 Release版本关键特性解析——Enhanced SWAP内存管理

    樊成阳 华为技术有限公司内核专家 陈杰 华为技术有限公司内核专家 OpenAtom OpenHarmony(以下简称"OpenHarmony")是面向全场景泛终端设备的操作系统,终 ...

  3. C++中的自定义内存管理

    1,问题: 1,new 关键字创建出来的对象位于什么地方? 1,位于堆空间: 2,有没有可能位于其它地方? 1,有: 2,通过一些方式可以使动态创建的对象位于静态存储区: 3,这个存储区在程序结束后释 ...

  4. delphi 自定义内存管理

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

  5. 《Effective C++》内存管理

    如果global new-hander没有成功配置,会抛出一个std::bad_alloc的exception. #include<iostream> #include<new> ...

  6. GC与显式内存管理

    C++复兴的话题至今已被鼓吹两年有余,Herb Sutter和Bjarne Stroustrup等大牛们也为C++带来了大步伐的革新.然而,从这两年的效果而言,C++的复兴并没有发生.一方面随着世界经 ...

  7. lua内存管理

    本文内容基于版本:Lua 5.3.0 Lua内存管理器规则 Lua允许用户自定义内存管理器,并在创建Lua虚拟机(lua_State实例)时传入.当然自定义内存管理器必须遵循Lua已定义的一些行为规则 ...

  8. 特定于类的内存管理---《C++必知必会》 条款36

    我们可以量身定制 operator new 和 operator delete 用于某个类类型,而不是必须使用标准版的 operator new 和 operator delete. 注意:我们不可以 ...

  9. libevent源码学习(2):内存管理

    目录 内存管理函数 函数声明 event-config.h 函数定义 event_mm_malloc_ event_mm_calloc_ event_mm_strdup_ event_mm_reall ...

随机推荐

  1. RHCE模拟考试

    真实考试环境说明: 你考试所用的真实物理机器会使用普通账号自动登陆,登陆后,桌面会有两个虚拟主机图标,分别是system1和system2.所有的考试操作都是在system1和system2上完成.S ...

  2. 六、EnterpriseFrameWork框架基础功能之权限管理

    回<[开源]EnterpriseFrameWork框架系列文章索引> 从本章开始进入框架的第二块内容“EnterpriseFrameWork框架的基础功能”,包括:权限管理.字典数据管理. ...

  3. .net mvc 使用ueditor的开发(官网没有net版本?)

    1.ueditor的下载导入 官网下载地址:https://ueditor.baidu.com/website/download.html · 介绍 有两种,一种开发版,一种Mini版,分别长这样: ...

  4. Appium+python的单元测试框架unittest(3)——discover(转)

    (原文:https://www.cnblogs.com/fancy0158/p/10047906.html) TestSuite套件可以添加很多个用例后运行,但是每个用例都需要调用addTest()函 ...

  5. [转]git命令之git remote的用法

    git remote git  remote -v git init git add xxx git commit -m 'xxx' git remote add origin ssh://softw ...

  6. 数据库sql优化总结之1-百万级数据库优化方案+案例分析

    项目背景 有三张百万级数据表 知识点表(ex_subject_point)9,316条数据 试题表(ex_question_junior)2,159,519条数据 有45个字段 知识点试题关系表(ex ...

  7. kubeadm构建k8s之Prometheus-operated监控(0.18.1)

    介绍: 大家好,k8s的搭建有许多方式,也有许多快速部署的,为了简化部署的复杂度,官方也提供了开源的kubeadm快速部署,最新1.10.x版本已经可以实现部署集群, 如果你对k8s的原理已经非常了解 ...

  8. 【C#】人脸识别 视频数据转图片数据

    使用虹软人脸识别的开发过程中遇到了转换的问题 因为不会用C#直接打开摄像头,就只能用第三方dll.一开始用Aforge,后来发现有个问题,关闭摄像头老是陷入等待,所以抛弃了.前一阵子开始用封装了Ope ...

  9. 在Emacs 23里字体的调整(转自ChinaUnix.net)

    首先,在Emacs中,通过菜单Options --> Set Default Font,设置好你喜欢的字体. 然后,把光标放到你所在的字体上,用命令M-x describe-font来查看你当前 ...

  10. SpringMVC Controller介绍及常用注解——@Controller

    一 在SpringMVC 中,控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Model ...