◆刘 晨
(青岛科大技术专修学院)
C++教学难点浅析
◆刘 晨
(青岛科大技术专修学院)
在对自考学生进行“C++程序设计”学习指导时,发现许多学生总是不能正确解答“动态分配内存”和“构造函数调用”这两个重要的知识点相关的问题。本文就这两个问题进行讨论。
1.1 问题的提出
动态分配内存几乎是所有应用程序的重要组成部分。C++提供的动态分配内存是由 new和 delete两个运算符实现的,在自考教材中多处提到,其中在第10页例 1.3和第 82页例 4.8两处出现如下:
上例中,两次动态定义的均是数组,但采用 delete释放内存的方式确不同,且上机运行均能通过编译,从而给人以两种方式均正确的认识。
但是,在 2009年 10月全国试卷第 49题中,对用 data=new T[len]定义的动态数组,给出的标准答案只有一个即“delete[]data;”而不是“delete []data;”或“delete data;”。到底怎样才是正确的呢?请看下面解答。
1.2 单个对象内存空间的动态分配和释放
1.2.1 动态分配
一般形式:数据类型*指针名 =new数据类型(初值);
语句作用:通过 new语句,动态分配一个能够存储指定数据类型数据的内存单元,并把分配到的内存初始化为某一个可知的初值,同时返回指向内存的开始处的指针。
注意:初始化部分的类型必须与内存分配的数据类型一致。
1.2.2 释放内存
一般形式:delete指针名;语句作用:告诉系统释放由 new分配的单个内存单元。
几点注意:
(1)用 new为指针分配内存,称为手工分配内存,这种方式分配的内存不能自动释放(由系统定义变量时分配的内存,当不再使用时,系统将会自动释放。),必须用 delete手工释放。
(2)delete必须用在先前已由 new成功分配内存的有效指针上,若用在了未用 new分配内存的指针上,将会带来严重的问题,比如系统崩溃。
(3)对于一个用 new分配内存的指针,只能用一次 delete。
1.3 数组的动态分配及释放
1.3.1 动态分配
一般形式:数据类型 *指针名 =new数据类型[m];
语句作用:通过 new语句动态分配一个由指定的数据类型所要求的连续m个内存单元,并返回指向首个单元开始处的指针。
注意:分配数组时不能给数组初始化,即不能给定初值。
1.3.2 释放内存
一般形式:数据类型 []指针名;
语句作用:告诉系统用[]释放动态分配的数组。
注意:
(1)用 delete释放一个动态数组时,还必须用[]形式告诉 delete释放多大的内空间,否则内存长期占用,很快就会耗尽,造成内存泄漏。
(2)用 delete释放由 new分配数组时,只需用[],不必在括号中加数字说明数组长度。
例2动态分配对象数组。
说明:释放对象数组时,用 delete[]形式,使对象数组中每个对象都调用一次析构函数,可将整个数组完整释放。但是,如果将此例中 delete[]A;改为 delete A;,则运行结果变为:
此时只对数组中第一个元素调用了析构函数,即“delete指针;”命令只能释放指针所指向的单元,不能为对象数组中每个对象都调用一次析构函数。
1.4 综述
通过对动态分配和释放单个内存和数组两种情况的讨论可知,“delete指针名;”命令只能释放由 new创建的单个存储单元空间,只有用“delete []指针名;”方式才能释放由 new创建的动态数组占用的所有空间。
教材在第5章和第6章分别介绍了含有对象成员的类对象和派生类对象调用构造函数的顺序问题,简述如下。
2.1 含有对象成员的类对象调用构造函数的顺序
2.1.1 知识点
在类的声明中,数据成员可以以另一种类为类型,称为对象成员。
在定义包含对象成员的类对象时,C++编译器为对象成员调用构造函数的顺序是按照对象成员在类中的声明顺序进行的,而与初始化列表中参数顺序无关。
例 3对象成员调用构造函数的顺序
例3说明
在类 container中,成员 one和 two都是 object类对象,声明顺序是 one、two。在有参构造函数初始化列表中one在后 two在前,但运行结果表明,系统是先为 one调用了 object类的有参构造函数,后为 two调用 object类的有参构造函数。从而验证了前面所述结论。
2.2 不含对象成员的派生类对象调用构造函数的顺序
知识点:
如果派生类中没有对象成员,派生类对象定义时,C++编译器也会自动调用派生构造函数,并通过派生构造函数调用直接基类构造函数,其顺序是:先调用基类构造函数,再调用派生类构造函数。
例4源程序
2.3 派生类有基类对象成员时派生类对象调用构造函数的顺序
2.3.1 问题的提出
当派生类的数据成员中有基类对象成员时,系统将按怎样的顺序调用构造函数呢?先看看例题5。
例 5派生中有基类对象成员时,定义派生对象时调用构造函数的顺序
例5运行结果
Point…1//为派生类对象 r调用基类 Point的有参构造函数
为继承的数据成员 x赋值。
Point…2//为 Point类对象成员 a调用 Point类的有参构造函数
Rectangle…//为派生类对象 r调用派生类 Rectangle构造函数
2
.3.2结论
由上例可见,当派生类中含有基类对象成员时,调用基类构造函数的顺序是:基类构造函数→对象成员应调用的构造函数→派生类构造函数。
综上所述,C++动态分配内存是由 new和 delete两个语句相互配合完成,在使用中应注意分清分配的是单个内存单还是数组,因为它们的空间在释放时方法不同。派生类对象和对象成员调用构造函数是有规律所循的。正确掌握和使用它们,对C++程序设计是非常重要的。
[1]刘振安.C++程序设计.北京:机械工业出版社,2008.86-167.
[2]甘玲,李盘林.解析 C++面向对象程序设计.北京:清华大学出版社.
[3]徐士良,葛兵,徐艳.C++程序设计.北京:机械工业出版社,2006. 283-345.
[4]曹静,董宁,陈丹.C++面向对象程序设计.北京:中国水力水电出版社.
[5]李春葆,陶红艳,金晶.C++语言程序设计.北京:清华大学出版社.
[6]刘玉英,张怡芳,王涛伟.程序设计基础 C++.北京:人民邮电出版社.