一、实验目的
1.掌握派生类的声明方法和派生类构造函数的定义方法
2.掌握不同方式下,构造函数与析构函数的执行顺序与构造规则
二、实验内容
(1)输入以下程序,写出程序运行结果
#include <iostream>
#include<string>
using namespace std;
class MyArray {
public:
MyArray(int length);
~MyArray();
void Input();
void Display(string);
protected:
int* alist;
int length;
};
MyArray::MyArray(int leng)
{
if (leng <= 0)
{
cout << "error length";
exit(1);
}
alist = new int[length];
length = leng;
if (alist == NULL)
{
cout << "assign failure";
exit(1);
}
cout << "MyArray类对象已创建!" << endl;
}
MyArray::~MyArray()
{
delete[] alist;
cout << "MyArray类对象已撤销!" << endl;
}
void MyArray::Display(string str)
{
int i;
int* p = alist;
cout << str << length << "个整数:";
for (i = 0; i < length; i++, p++)
cout << *p << "";
cout << endl;
}
void MyArray::Input()
{
cout << "请从键盘输入" << length << "个整数:";
int i;
int* p = alist;
for (i = 0; i < length; i++, p++)
{
cin >> *p;
}
}
int main()
{
MyArray a(5);
a.Input();
a.Display("显示已经输入的");
return 0;
}
首次运行结果:
错误分析与解决:
在length 赋值之前,就给alist分配了内存空间,此时length的数字是个随机数,在执行delete[] alist时,释放内存空间错误,导致基类的析构函数也无法执行。将
alist = new int[length];
length = leng;
改为
alist = new int[leng];
length = leng;
或者交换这两条语句
length = leng;
alist = new int[leng];
这时,在给alist分配了内存空间之前,就给变量已赋值,程序能正常运行。
正确运行结果:
结果分析:
以上程序是在类中声明了构造函数、析构函数及其他成员函数,在类外对它们进行声明,在定义类的对象时,自动调用了构造函数,然后执行其他成员函数,在函数整体执行结束时,自动调用析构函数,执行结果如图。
(2)派生类的声明和派生类构造函数的定义
声明一个派生类的一般格式为:
class 派生类名:[继承方式] 基类名 {
派生类新增的数据成员和成员函数
};
这里,“基类名”是一个已经声明的类的名称,“派生类名”是继承原有类的特性而生成的新类的名称。“继承方式”规定了如何访问从基类继承的成员,它可以是关键字 private,protected 或 public,分别表示私有继承、保护继承和公有继承。如果不显示地给出继承方式关键字,系统默认为私有继承。
派生类构造函数的定义:
派生类名(参数总表):基类名(参数表 )
{
派生类新增数据成员的初始化语句
}
冒号前面的部分是派生类构造函数的主干,和构造函数的形式相同,但它的总参数表中包括基类构造函数所需的参数和对派生类新增的数据成员初始化所需要的
参数;冒号后面的部分是要调用的基类构造函数及其参数。
(1) 可以将派生类构造函数定义在类的外部,而在类体内只写该函数的声明。
(2) 若基类使用默认构造函数或不带参数的构造函数,则在派生类中定义构造函数时可略去“:基类构造函数名(参数表)”,此时若派生类不需要构造函数,则可不定义派生类构造函数。
(3) 当基类构造函数不带参数时,派生类不一定需要定义构造函数,当基类的构造函数哪有参数时,它所有的派生类都必须定义构造函数。
示例代码:
#include <iostream>
#include<string>
using namespace std;
class MyArray {
public:
MyArray(int length);
~MyArray();
void Input();
void Display(string);
protected:
int* alist;
int length;
};
class SortArray :public MyArray {
public:
SortArray(int length) : MyArray(length)
{
cout << "SortArray类对象已创建!" << endl;
}
~SortArray()
{
cout << "SortArray类对象已撤销!" << endl;
}
void sortarray();
};
MyArray::MyArray(int leng)
{
if (leng <= 0)
{
cout << "error length";
exit(1);
}
length = leng;
alist = new int[length];
if (alist == NULL)
{
cout << "assign failure";
exit(1);
}
cout << "MyArray类对象已创建!" << endl;
}
MyArray::~MyArray()
{
delete[] alist;
cout << "MyArray类对象已撤销!" << endl;
}
void MyArray::Display(string str)
{
int i;
int* p = alist;
cout << str << length << "个整数:";
for (i = 0; i < length; i++, p++)
cout << *p << " ";
cout << endl;
}
void MyArray::Input()
{
cout << "请从键盘输入" << length << "个整数:";
int i;
int* p = alist;
for (i = 0; i < length; i++, p++)
{
cin >> *p;
}
}
void SortArray::sortarray()
{
int temp;
int* p = alist;
for (int i = 0; i < length; i++)
{
for (int j = i+1; j < length; j++)
{
if (*(p+i) > *(p + j))
{
temp = *(p + i);
*(p + i) = *(p + j);
*(p + j) = temp;
}
}
}
}
int main()
{
SortArray a(5);
a.Input();
a.Display("显示已经输入的");
a.sortarray();
a.Display("排序后的");
return 0;
}
运行结果:
运行结果分析:
在上个程序的基础上又声明了派生类,派生类中也声明了构造函数、析构函数和成员函数。用派生类定义对象,先执行了基类的构造函数,在执行派生类的构造函数。析构函数的调用顺序与构造函数正好相反:先执行派生类的析构函数,再执行基类的析构函数。派生类中的成员函数为排序函数,对输入的五个数据进行从小到大排序。
三、实验总结
1.用new分配内存空间,要注意内存空间是否够用。要为已知的数据分配空间,否则会发生错误。
2.派生类不能继承基类中的构造函数和析构函数。当基类含有带参数的构造函数时,派生类必须定义构造函数,以提供把参数传递给基类构造函数的途径。
3.构造函数和析构函数的执行顺序是先执行基类的构造函数,执行派生类的构造函数,析构函数的调用顺序与构造函数正好相反:先执行派生类的析构函数,再执行基类的析构函数。