C++类和new、delete操作符和堆和栈内存的分配

如果你是Java、C#、PHP程序员,那么会对 new 非常熟悉,在这些编程语言中,只能通过 new 来创建对象。
在C++中,你可以像定义变量一样来创建对象,如:

    
    
  1. Student stu; //对象已被实例化,已分配内存空间,可以使用了
  2. stu.say(); //调用成员函数
这种情况下,系统会在栈区为对象分配内存。栈区是内存中的一块区域,由系统自动分配和释放,程序员无法操控,一般用来存放函数的参数值、局部变量、局部对象等。

当发生函数调用时,系统将函数参数、局部变量、局部对象依次压入栈区;函数执行结束,再按照先进后出的原则将它们弹出(销毁)。

对于大部分程序,这不会有什么问题。但当你希望在函数调用结束前销毁对象时,你是无能为力的。或者你希望通过 for 循环来创建多个对象,这种方法同样也做不到。

这个时候 new 和 delete 就派上了用场:使用 new 创建的对象,可以在任意时刻通过 delete 销毁,而且只需要一个指针指向它。

以前面的 Student 类为例,可以这样来动态创建对象:
new Student;
也可以使用构造函数:
new Student("小明", 15, 90.5f);
这样,就在堆区为对象分配了内存,并调用了构造函数。

但是此时程序员还无法访问这个对象,因为这个对象既没有名字,也没有指针指向它。这种对象称为 匿名对象 ,它确实存在,但无法访问。

用一个指针来指向Student类的对象:
Student *pStu;
pStu = new Student("小明", 15, 90.5f);
或者:
Student *pStu = new Student("小明", 15, 90.5f);

当不再需要对象时,可以通过 delete 销毁:
delete pStu;
这样,就释放掉了对象占用的内存,并调用了析构函数。

需要说明的是:new 在堆区为对象分配内存。与栈区不同的是,堆区内存由程序员分配和释放,系统不会自动销毁,即使函数调用结束了,仍然会保留堆区内存。如果程序员不主动回收堆区内存,那么只能在程序运行结束后由操作系统回收。

为了避免内存泄露,强烈建议 new 和 delete 成对出现,及时销毁不再需要的对象。

例如,下面的代码会造成严重的内存泄露:

    
    
  1. #include <iostream>
  2. #include <cstdlib>
  3. using namespace std;
  4. class Demo{
  5. private:
  6. double n;
  7. double m;
  8. int i;
  9. };
  10. void func(){
  11. Demo *p = new Demo;
  12. }
  13. int main(){
  14. int i;
  15. for(i=1; i<=1000000; i++){
  16. func();
  17. }
  18. system("pause");
  19. return 0;
  20. }
当程序运行到 system("pause"); 语句时,你可以打开任务管理器,会发现这个小小的程序竟然占用了 32M 内存。

这是因为每次调用 func 函数,都会创建一个对象,并用 p 指向它。函数运行结束,仅仅释放了指针变量 p 占用的内存,而没有释放 p 所指向的对象占用的内存。

如果在 func 函数中不回收对象内存,那么你将永远无法回收,只能等到程序运行结束由操作系统回收,这就是典型的内存泄露。

另外注意,C语言中的 malloc、free 函数不能用来为对象分配和释放内存。请看下面的例子:

    
    
  1. #include <iostream>
  2. using namespace std;
  3. class Demo{
  4. public:
  5. Demo();
  6. ~Demo();
  7. };
  8. Demo::Demo(){
  9. cout<<"Constructor"<<endl;
  10. }
  11. Demo::~Demo(){
  12. cout<<"Destructor"<<endl;
  13. }
  14. int main(){
  15. cout<<"------new------"<<endl;
  16. Demo *p1 = new Demo; //创建一个对象
  17. Demo *p2 = new Demo[5]; //创建一组对象
  18. cout<<"------malloc------"<<endl<<endl;
  19. Demo *p3 = (Demo*)malloc(sizeof(Demo));
  20. cout<<"------delete------"<<endl;
  21. delete p1; //销毁一个对象
  22. delete[] p2; //销毁一组对象
  23. cout<<"------free------"<<endl;
  24. free(p3);
  25. return 0;
  26. }
运行结果:
------new------
Constructor
Constructor
Constructor
Constructor
Constructor
Constructor
------malloc------

------delete------
Destructor
Destructor
Destructor
Destructor
Destructor
Destructor
------free------

从程序运行结果可以看出:malloc 虽然分配了内存,但没有调用构造函数;free 虽然释放了内存,但也没有调用析构函数。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最好的最简单的Java入门教程。 目录 1 Java概述 1 1.1 Java语言概述 1 1.2 Java虚拟机以及跨平台原理 2 1.3 关于JVM的执行效率 2 1.4 客户端市场的折戟 3 1.5 Java的主要就业方向 3 1.5.1.1 Web开发 3 1.5.1.2 Android开发 3 1.5.1.3 客户端开发 3 1.6 Java的不同版本 4 1.6.1.1 J2SE(Java 2 Platform Standard Edition) 标准版 4 1.6.1.2 J2EE(Java 2 Platform Enterprise Edition) 企业版 4 1.6.1.3 J2ME(Java 2 Platform Micro Edition) 微型版 4 1.7 Java开发环境搭建 4 1.8 JDK 的下载 4 1.9 JDK的安装 5 1.10 环境变量的设置 9 1.11 Eclipse的安装 9 1.12 安装简体文语言包 11 1.13 第一个Java程序示例 14 1.14 通过Eclipse运行程序 14 1.15 通过命令行运行程序 17 1.16 HelloWorld程序分析 18 1.17 Java和对象的概念 18 1.18 面向对象编程(Object Oriented Programming, OOP) 19 1.19 Java库及其组织结构 21 1.20 Java import及Java的搜索路径 22 1.21 Java的搜索路径 23 2 Java语法基础 24 2.1 Java数据型以及变量的定义 24 2.2 对布尔型的说明 25 2.3 Java数据型转换 26 2.4 自动数据型转换 26 2.5 强制数据型转换 26 2.6 Java运算符 27 2.6.1.1 数学运算符 27 2.6.1.2 关系运算符 27 2.6.1.3 位运算符 27 2.6.1.4 条件运算符 28 2.7 Java流程控制 29 2.8 Java数组的定义和使用 31 2.9 数组的定义和内存分配 31 2.10 数组的初始化 31 2.11 数组引用 32 2.12 数组的遍历 32 2.13 二维数组 33 2.14 Java字符串(String) 35 2.15 字符串操作 35 2.15.1.1 1) length() 方法 36 2.15.1.2 2) charAt() 方法 36 2.15.1.3 3) contains() 方法 36 2.15.1.4 4) replace() 方法 36 2.15.1.5 5) split() 方法 36 2.16 Java StringBuffer与StringBuider 37 2.17 StringBuffer的主要方法 37 2.17.1.1 1) append() 方法 37 2.17.1.2 2) deleteCharAt() 37 2.17.1.3 3) insert() 方法 38 2.17.1.4 4) setCharAt() 方法 38 2.18 String和StringBuffer的效率对比 38 2.19 StringBuilder 39 2.20 总结 39 2.21 强调一下编程风格 40 3 Java与对象 42 3.1 Java的定义及其实例化 42 3.2 构造方法 42 3.3 创建对象 43 3.4 访问成员变量和方法 44 3.5 Java访问修饰符 45 3.6 public:公有的 45 3.7 protected:受保护的 45 3.8 private:私有的 46 3.9 默认的:不使用任何关键字 47 3.10 访问控制和继承 47 3.11 如何使用访问控制符 47 3.12 Java变量的作用域 48 3.13 Java this关键字详解 49 3.14 使用this区分同名变量 49 3.15 作为方法名来初始化对象 50 3.16 作为参数传递 51 3.17 Java方法重载 52 3.18 Java的基本运行顺序 53 3.19 Java包装、拆箱和装箱详解 54 3.20 包装的应用 54 3.20.1.1 1) 实现 int 和 Integer 的相互转换 54 3.20.1.2 2) 将字符串转换为整数 55 3.20.1.3 3) 将整数转换为字符串 55 3.21 自动拆箱和装箱 56 3.22 再谈Java包 56 3.23 如何实现包 56 3.24 包的调用 56 3.24.1.1 1) 在每个名前面加上完整的包名 57 3.24.1.2 2) 通过 import 语句引入包 57 3.25 的路径 57 3.26 包的访问权限 58 3.27 源文件的声明规则 59 3.28 一个简单的例子 59 4 Java继承和多态 61 4.1 继承的概念与实现 61 4.2 Java super关键字 63 4.3 调用隐藏变量和被覆盖的方法 63 4.4 调用父的构造方法 64 4.5 继承的方法的覆盖和重载 65 4.6 多态和动态绑定 66 4.7 动态绑定 69 4.8 instanceof 运算符 70 4.9 多态对象的型转换 71 4.10 Java static关键字以及Java静态变量和静态方法 72 4.11 static 的内存分配 73 4.12 静态方法 74 4.13 静态初始器(静态块) 75 4.14 静态导入 75 4.15 Java final关键字:阻止继承和多态 76 4.16 之间的关系 78 4.17 依赖(uses-a) 78 4.18 聚合(has-a) 78 4.19 继承 79 4.20 Java Object 79 4.21 equals() 方法 79 4.22 hashCode() 方法 80 4.23 toString() 方法 80

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值