●掌握派生类的声明方法和派生类构造函数的定义方法
●掌握不同方式下,构造函数与析构函数的执行顺序与构造规则
1、派生类的声明
Class 派生类:[继承方式] 基类名{
派生类新增的数据成员和成员函数
};
如果不显式地给出继承方式关键字,系统默认为私有继承(private)。类的继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限。
派生类的工作包括三部分:
(1)派生类从基类接收成员。
(2)调整从基类接受来的成员。
(3)在派生类中增加新的成员。
2、构造函数的定义
派生类名(参数总表):基类名(参数表){
派生类新增数据成员的初始化语句
}
冒号后面的部分是要调用的基类构造函数及其参数。
(1)可以将派生类构造函数定义在类的外部,而在类体内只写该函数的声明。(在类中声明派生类构造函数时,不包括基类构造函数名及其参数表,只在类外定义构造函数时才将它列出)
(2)若基类使用默认构造函数或不带参数的构造函数,则在派生类中定义构造函数时,可略去“:基类构造函数名(参数表)”,此时若派生类不需要构造函数,则可不定义派生类构造函数。
(3)当基类构造函数不带参数时,派生类不一定需要定义构造函数,然而当基类的构造函数哪怕只带有一个参数,它所有的派生类都必须定义构造函数,甚至所定义的派生类构造函数的函数体可能为空,仅仅起参数的传递作用。
使用以下代码,观察其执行结果。
#include<iostream> 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; }
其结果是
这是典型的内存溢出错误,常在内存的delete处发生。这是因为在新建立alist内存空间时,有两句语句的顺序出现错误,length = leng; alist = new int[length];,这时候length的数字是个随机数,在最后delete释放的时候,其会出现错误的情况。
因此先保证用new分配的内存空间足够,就不会导致这个问题,因此在两条语句互换一下位置便能够使程序正常运行。其析构函数能够被正常的执行了。
●声明一个SortArray继承类MyArray,在该类中定义一个函数,具有将输入的整数从小到大进行排序的功能
代码如下:
#include<iostream> 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); } 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; } class SortArray :public MyArray { public: SortArray(int leng); ~SortArray(); void rank(); }; SortArray::SortArray(int leng):MyArray(leng) { cout << "SortArray类对象已经创建!" << endl; } SortArray::~SortArray() { cout << "SortArray类对象已撤销!" << endl; } void SortArray::rank() { Display("显示排序之前"); int* p = alist; for (int i = 0; i < length; i++) { for (int j = i + 1; j < length; j++) if (*(p + i) > *(p + j)) { int temp = *(p + i); *(p + i) = *(p + j); *(p + j) = temp; } } Display("显示排序之后"); } int main() { SortArray a(5); a.Input(); a.Display("显示已经输入的"); a.rank(); return 0; }
其运行结果取下:
由这个代码,我们可以得出在派生类中,构造函数与析构函数的执行顺序。通常情况下,当创建派生类对象时,首先执行基类的构造函数,随后再执行派生类的构造函数;当撤销派生类对象时,则先执行派生类的析构函数,随后再执行基类的析构函数。
#include<iostream> 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); } 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; } class SortArray :public MyArray { public: SortArray(int leng); ~SortArray(); void rank(); }; SortArray::SortArray(int leng):MyArray(leng) { cout << "SortArray类对象已经创建!" << endl; } SortArray::~SortArray() { cout << "SortArray类对象已撤销!" << endl; } int maxbit(int* a, int n) { //找出数据中在大位数是几位 int d = 1; //保存最大的位数 int p = 10; for (int i = 0; i < n; ++i) { while (*(a + i) >= p) { p *= 10; ++d; } } return d; } void radixsort(int *a,int n) {//基数排序 int d = maxbit(a, n); int *temp = new int[n]; int* count = new int[10]; //计数器 int i, j, k; int radix = 1; for (i = 0; i <= d; i++) { //进行d次排序 for (j = 0; j < 10; j++) count[j] = 0; //每次分配前清空计数器 for (j = 0; j < n; j++) { k = (*(a+j) / radix) % 10; //统计每个桶中的记录数 count[k]++; } for (j = 1; j < 10; j++) count[j] = count[j - 1] + count[j];//将temp中的位置依次分配给每个桶 for (j = n - 1; j >= 0; j--) {//将所有桶中记录依次收集到temp k = (*(a + j) / radix) % 10; temp[count[k] - 1] = *(a + j); count[k]--; } for (j = 0; j < n; j++)//将临时数组的内容复制到*a中 *(a + j) = temp[j]; radix = radix * 10; } delete[]temp; delete[]count; cout << "基数排序后的结果" << n << "个整数:"; for (int i = 0; i <n; i++, a++) cout << *a << " "; cout << endl; } void SortArray::rank() { Display("显示排序之前"); int* p = alist; radixsort(p, length); } int main() { SortArray a(5); a.Input(); a.Display("显示已经输入的"); a.rank(); return 0; }
这时输出的结果为:
基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。 其不能用作负数。
其原理为,先找到这一串数列的最大位数是几位,在按照个位排序,十位排序,百位排序,最后实实现了顺序。原理如下图:
(1)在写派生类的时候,要注意继承方式对派生类访问基类中的数据的规则影响。
(2)在声明派生类的构造函数,需要掌握其声明规则,哪些时候需要“:基类构造函数名(参数表)”等这些要区分清楚。
(3)在排序的时候,可以多选择几种排序方法,可以多尝试。
(4)在用new分配内存空间的时候,要注意其分配的空间是否够用,会不会导致溢出。