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. 【BZOJ4803】逆欧拉函数

    [BZOJ4803]逆欧拉函数 题面 bzoj 题解 题目是给定你\(\varphi(n)\)要求前\(k\)小的\(n\). 设\(n=\prod_{i=1}^k{p_i}^{c_i}\) 则\(\ ...

  2. 自己用C语言写RL78 serial bootloader

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). 之前用C实现过多款PI ...

  3. asp.net core添加全局异常处理及log4net、Nlog应用

    0.目录 整体架构目录:ASP.NET Core分布式项目实战-目录 一.介绍 此篇文章将会介绍项目的全局异常收集以及采用log4net或者NLog记录. 众所周知,一旦自己的项目报错,如果没有进行处 ...

  4. WPF RegisterAttached ListBoxItem(附加属性传递到Item)

    /// <summary> /// Controls的附加属性 /// </summary> public class ControlsAttached : Dependenc ...

  5. javaweb(二十四)——jsp传统标签开发

    一.标签技术的API 1.1.标签技术的API类继承关系 二.标签API简单介绍 2.1.JspTag接口 JspTag接口是所有自定义标签的父接口,它是JSP2.0中新定义的一个标记接口,没有任何属 ...

  6. Charles连接苹果及JSON乱码情况解决

    1.  Charles的JSON乱码情况解决: 点击Charles界面上的help—SSL proxying—install Charles Root Certificate,将证书安装到[受信任的根 ...

  7. 第六篇 native 版本的Postman如何通过代理服务器录制Web及手机APP请求

    第四篇主要介绍了chrome app版本的postman如何安装及如何录制Web脚本,比较简单. 但是chrome app 版本和native 版本相比,对应chrome app 版本官方已经放弃支持 ...

  8. Centos下安装并设置nginx开机自启动

    一.在centos环境下安装下载并安装nginx,由于nginx需要依赖一些环境才能安装,主要依赖g++.gcc.openssl-devel.pcre-devel和zlib-devel这些环境,首先得 ...

  9. mongodb复制集部署文档

    一.安装SNMP(新版mongodb需要此依赖安装) 安装snmp服务需要的rpm包: perl-Data-Dumper-2.145-3.el7.x86_64.rpm net-snmp-5.7.2-2 ...

  10. 高可用Kubernetes集群-7. 部署kube-controller-manager

    九.部署kube-controller-manager kube-controller-manager是Kube-Master相关的3个服务之一,是有状态的服务,会修改集群的状态信息. 如果多个mas ...