C++内存管理的缩影
都说C++内存管理是个大坑。实际上也确实是这样。
C++有析构函数,每当一个对象过期的时候,C++会执行两个动作
1.执行析构函数。
2.将对象和对象的所有数据删除。
很多人就会问了,既然有把对象删除的操作,要析构函数何用?我一开始也有一样的疑问,但是!我们都知道C++有一种神奇的类型,指针!指针他就是一个4字节的变量,甚至可以转化成int等类型打印。明白这个道理,就知道C++析构函数的作用了。
delete关键字,他后边可以接一个指针,也可以接一个例如 delete []array 这样的数组变量,其实意义都一样,它寻找的都是一个或者一组地址,把它指向的堆内存释放掉。
看一组类定义:
#pragma once #include<string> struct BattleValue { public: int atk; int def; std::string name; std::string desc; BattleValue(); ~BattleValue(); }; class Role { public: Role(); Role(int atk, std::string name); ~Role(); Role(const Role& object); friend void show(Role role); BattleValue* data; };
这个例子中,我们看到Role类有一个变量,名字叫做data,类型是BattleValue类型的指针。其实我们在使用这个Role类的对象的时候,可以选择是否给这个data赋值一个有效值。但是有一点千万要注意,即便像构造函数中的那样给data赋值了,也不过给他赋值了一个4字节的指针变量,所以看一下源文件。
#include<iostream> #include"Role.h" using std::cout; using std::endl; BattleValue::BattleValue() { cout<<"Battle init"<<endl; } BattleValue::~BattleValue() { cout<<"Battle free"<<endl; } Role::Role() { cout<<"default role"<<endl; data = new BattleValue(); } Role::Role(int atk, string name) { cout<<"parameter role"<<endl; data = new BattleValue(); data->atk = atk; data->name = name; } Role::Role(const Role& object) { cout<<"copy"<<endl; data = new BattleValue(); data->atk = object.data->atk; data->name = object.data->name; } Role::~Role() { cout<<"free self"<<endl; delete data; } void show(Role role) { cout<<"name:"<<role.data->name<<" atk:"<<role.data->atk<<endl; } int main() { Role rock(, "RockDeria"); show(rock); system("pause"); }
这段代码执行后是这样的结果
parameter role Battle init copy Battle init name:RockDeria atk: free self Battle free 请按任意键继续. . . free self Battle free
因为我是在windows下调试的,所以上述的结果的最后两行在cmd是几乎看不见的,一闪而过窗口就关闭了。这不影响我们来分析。
在程序开始的时候,利用参数的构造方法创建了role对象,然后在Role的构造方法中创建了一个BattleValue对象,然后我们取这个对象的地址赋值给了data变量。我们接下来调用Role类的友元函数,因为是传值参数调用,所以调用show方法的时候对role进行了复制。
复制的时候有调用了Role类的拷贝构造函数。我们在拷贝构造函数Role(const Role& object);中可以看到我们重新给data赋值,一个新的内存地址。
读到这里,想必大家都已经了解的差不多了。在对象过期的时候,是在调用析构函数之后把对象的所有数据都删除,但是*data是什么?不知道,C++只知道data,不会去解引用,只负责把这个4字节删除了。所以说,BattleValue对象的堆呢?GC何在?没有,不好意思内存就此泄漏。
所以我们才在Role的析构函数中追加了一个delete data 这样的一个操作,我们可以看到,它的作用也是两点
1.调用BattleValue类的析构函数
2.把BattleValue对象的所有数据都删除
只有这么做,我们才能做到所谓的我们想要的结果,那就是一个Role对象一个BattleValue对象,Role死BattleValue死。Role生BattleValue生。
其实这是为了方便理解,若吾早知如此,那完全可以不用这么麻烦,直接存一个BattleValue对象在Role类当中,不要放指针,这样C++会帮我们处理内存。当然,前提是需求是
一个Role对象一个BattleValue对象,Role死BattleValue死。Role生BattleValue生。但如果需求不是呢?如果所有的角色当中,弓箭手用一套战斗力,战士用一套战斗力,法师用一套战斗力。。。。。。那么你每一个角色对象都给他一个战斗力子对象对内存来说是不是太浪费了?
这个时候又该说,还是指针实在!但是内存又要如何管理了?
都说C++内存管理是个大坑。实际上也确实是这样。
C++内存管理的缩影的更多相关文章
- .net core中的高效动态内存管理方案
.net core在新增的System.Buffers中引入了一大堆高效内存管理的类,如span和memory.内存池.本文今天这里介绍一个高效动态内存访问方案. ReadOnlySequenceSe ...
- .NET基础拾遗(1)类型语法基础和内存管理基础
Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开发基 ...
- PHP扩展-生命周期和内存管理
1. PHP源码结构 PHP的内核子系统有两个,ZE(Zend Engine)和PHP Core.ZE负责将PHP脚本解析成机器码(也成为token符)后,在进程空间执行这些机器码:ZE还负责内存管理 ...
- linux2.6 内存管理——逻辑地址转换为线性地址(逻辑地址、线性地址、物理地址、虚拟地址)
Linux系统中的物理存储空间和虚拟存储空间的地址范围分别都是从0x00000000到0xFFFFFFFF,共4GB,但物理存储空间与虚拟存储空间布局完全不同.Linux运行在虚拟存储空间,并负责把系 ...
- linux2.6 内存管理——概述
在紧接着相当长的篇幅中,都是围绕着Linux如何管理内存进行阐述,在内核中分配内存并不是一件非常容易的事情,因为在此过程中必须遵从内核特定的状态约束.linux内存管理建立在基本的分页机制基础上,在l ...
- Objective-C内存管理之引用计数
初学者在学习Objective-c的时候,很容易在内存管理这一部分陷入混乱状态,很大一部分原因是没有弄清楚引用计数的原理,搞不明白对象的引用数量,这样就当然无法彻底释放对象的内存了,苹果官方文档在内存 ...
- Quartz2D内存管理
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px "PingFang SC"; color: #239619 } p.p2 ...
- 浅谈Linux内存管理机制
经常遇到一些刚接触Linux的新手会问内存占用怎么那么多?在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然.这是Linux内存管理的一个优秀特性,在这 ...
- linux内存管理
一.Linux 进程在内存中的数据结构 一个可执行程序在存储(没有调入内存)时分为代码段,数据段,未初始化数据段三部分: 1) 代码段:存放CPU执行的机器指令.通常代码区是共享的,即其它执行程 ...
随机推荐
- A ship is always safe at the shore - but that is not what it is built for.
A ship is always safe at the shore - but that is not what it is built for. 船靠岸边总是安全的,但那不是建造它的目的.
- Active MQ 传输 ObjectMessage 异常
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFa ...
- centos中基于随机数,再加入班级学生姓名
这只需要在上一篇的随机数中加入数值就可以了 代码如下 #!/bin/bash num=$(date +%N); c=(wanghao xieyunsheng) a=`expr $num % 39 ` ...
- xampp修改mysql默认密码详解
在这里介绍xampp修改mysql默认密码的大概过程是先利用xampp的phpmyadmin进入修改mysql密码,修改之后我们再修改xampp中phpmyadmin的密码,这样就完整的修改mysql ...
- Hibernate的映射组件属性
组件属性 如果持久化类的属性并不是基本数据类型,也不是一个日期或者字符串常量,而是一个复合类型的对象,例如 private Name name; 那么这就是一个组件属性. 组件属性可以是任何普通的ja ...
- JDK动态代理的实现原理
学习JDK动态代理,从源码层次来理解其实现原理参考:http://blog.csdn.net/jiankunking/article/details/52143504
- Chapter 2: 随机变量
1. 随机变量, 离散型随机变量,连续型随机变量 设$\Omega$为随机试验的样本空间,$X:\Omega \rightarrow R$是定义在样本空间$\Omega$上的实值函数,则称$X$为随机 ...
- ubuntu安装(owncloud-docker安装)
1.源下载地址:http://mirrors.aliyun.com/ubuntu-releases/ ubuntu-16.04-server-amd64.iso 2.在虚拟机上安装完后 (1)查看IP ...
- [POJ2420]A Star not a Tree?(模拟退火)
题目链接:http://poj.org/problem?id=2420 求费马点,即到所有其他点总和距离最小的点. 一开始想枚举一个坐标,另一个坐标二分的,但是check的时候还是O(n)的,复杂度相 ...
- XPath使用实例
实例 1基本的XPath语法类似于在一个文件系统中定位文件,如果路径以斜线 / 开始, 那么该路径就表示到一个元素的绝对路径 //BBB 选择所有BBB元素 /AAA/CCC 选择 ...