以下为个人学习笔记和习题整理
课程:计算机程序设计(C++)- 西安交通大学 @ 中国大学 MOOC
https://www.icourse163.org/course/XJTU-46006

# 课堂笔记

采用继承式的抽象方法描述万物,减少代码重复。

class person
{
	protected:
		char *name; // 人名
		char sex; // 性别
		char pid[19]; // 身份证号码
		int weight; // 体重
		int high; // 身高
	public:
		person(); // 无参构造函数
		person(char *n,char s,char *p,int w,int h); // 有参构造函数
		void change_data(char *n,char s,char *p,int w,int h);// 修改数据
		void walking(int k,int v); // 以 v 速度行走 k 步
		void hearing(char *sentence); // 将字符串小写变大写,大写变小写输出
		void speek(int n); // 说出整数 num 的英文句子
		void writing(); // 在屏幕上画出汉字 “曲”
		void print(); // 输出人的属性值
		void out(int a);// 翻译小于 1000 的整数
		~person(); // 析构函数
}
// 模拟行走:以 v 档速度水平行走 k 步
void person::walking(int k,int v)
{
	cout<<"\n"<<name<<"水平直线行走"<<k<<"步"<<endl;
	for(int i=0;i<k;i++)
	{
		cout<<' '<<"o_o";
		Sleep(1000/v);
		cout<<"\b\b\b";
	}
}
// 模拟收听:将句子字母大变小,小变大
void person::hearing(char *sentence)
{
	cout<<endl<<sentence<<endl;
	char *p=new char[strlen(sentence)+1];
	strcpy(p,sentence);
	char *pp=p;
	while(*p)
	{
		if(*p>='a' && *p<='z')
		{
			*p='A'+(*p -'a'+0);
		}
		else if(*p>='A' && *p<='Z')
		{
			*p='a'+(*p-'A');
		}
		p++;
	}
	cout<<pp<<endl;
	delete pp;
}
// 模拟说话:说出整数的英文句子
void person::speek(int n)
{
	if(n>1999999999)
	{
		cout<<"dev C++平台无法处理大于1999999999位的数!"<<endl;
	}
	else
	{
		// 三位三位取出,存入 abcd 中
		int a=n/1000000000,b=(n%1000000000)/1000000
		int c=(n%1000000)/1000,d=n%1000;
		if(a!=0)
		{
			out(a);
			cout<<"billion ";
		}
		if(b!=0)
		{
			out(b);
			cout<<"million ";
		}
		if(c!=0)
		{
			out(c);
			cout<<"thousand ";
		}
		if(d!=0)
		{
			// 据英文语法规则,最后两位前一定有 and
			if(d<100&&(a!=0||b!=0||c!=0))
			{
				cout<<"and ";
			}
			out(d);
		}
		cout<<endl;
	}
}
int main()
{
	// 创建对象
	person Jack("James Chen",'M',"610103198901062493",160,180);
	Jack.print(); // 输出人的属性值
	Jack.walking(20,4); // 行走 20 步,1/4 秒走一步
	Jack.hearing("Hi! You are simple!"); // 听英文句子
	Jack.speek(1006); // 说出整数 1006 的英文句子
	cout<<endl;
	//Jack.writing (); // 书写汉字 “曲”
	return 0;
}

# 继承

# 概念

从一个或多个以前定义的类 (基类) 产生新类的过程称为派生,这个新产生的类又称为派生类。

类的继承(inheritance)是指新类从基类那里得到基类的特征,也就是继承基类的数据和函数。

派生的新类同时也可以增加或重新定义数据和函数,这就产生了类的层次性

派生和继承的概念也来自于人们认识客观世界的过程

# 好处

  • 软件复用是软件设计中常用的手段

  • 在程序设计中反复使用高质量的软件来缩短开发时间,提高效率(数量和质量)

  • 客观世界中许多实体之间是有继承特性的
    点→圆→圆柱体
    人→学生→大学生
    水果→桃→水蜜桃→陕西水蜜桃

# 派生类

采用已存在的类去定义建立新类
新类称为派生类(子类)
已存在的类称为基类(父类)
派生类与基类具有相对性

# 定义方法

class <派生类名>: <访问权限> <基类名1>,......<访问权限> <基类名n> 
{
	private:
		<新增私有数据成员和成员函数>
	protected:
		<新增保护数据成员和成员函效>
	public:
		<新增公有数据成员和成员函效>
};
  • 派生出新类时,可以做如下几种变化:

    1. 可以增加新的数据成员
    2. 可以增加新的函数成员
    3. 可以重新定义已有的函数成员
    4. 可以改变现有成员的数据值
  • 派生类的作用:

    1. 从基类接受成员
    2. 派生类对基类的扩充
    3. 派生类对基类成员的改造
    4. 系统的默认值就是私有继承
歌星类
class sing_star:public person
{
	protected:
		float salary; // 薪水
	public:
		sing_star(); // 无参构造函数
		sing_star(char *n,char s,char *p,int w,int h,float s1); // 有参构造函数
		void change_data(char *n,char s,char *p,int w,int h,float s1); // 修改数据
		void playing(char *ps); // 演唱歌曲
		void print(); // 输出歌星属性值
};
// 模拟唱歌:播放 mp3 歌曲
void sing_star::playing(char *ps)
{
	char str[100]="play "; //play 后有空格
	strcat(str,ps);
	cout<<str;
	mciSendString(str,NULL,0,NULL);
	// 在 Dec-C++ 环境中还要进行设置:工具 \ 编译器选项 \ 编译器 \ 在连接器命令,	加入以下命令 \-lwinmm
	//mciSendStringA (str,NULL,0,NULL); //Windows API VC2008 调	用此函数
	//mciSendString (str,NULL,0,NULL); //Windows API VC6.0 调用此函数
	char a;
	cin>>a; // 输入任何字符结束播放
}

# 内嵌对象

日期类的定义
class Date
{
	protected:
		int year
		int month;
		int day;
	public:
		Date()
		{
			year = 1900;
			month = day = 1;
		}
		Date(int yy,int mm,int dd)
		{
			init(yy,mm,dd);
		};
		void init(int,int,int );
		void print_ymd();
		void print_mdy();
};
时间类的定义
class Time
{
	protected:
		int hour;
		int miniter;
		int second;
	public:
		Time()
		{
			hour = miniter = second = 0;
		}
		Time(int h,int m,int s)
		{
			init(h,m,s);
		};
		void init(int,int,int);
		void print_time();
};
class person:public Date,public Time
{
		// 注意包含了基类的出身日期和出身时间
		char name[20];
		char sex;
		char pid[19];
		int weight;
		int hight;
	public:
		person();
		person(char *n,char s,char *p,int w,int h,int hr,int mr,int sd);
		void change_data(char *n,char s,char *p,int w,int h,int hr,int mr,int sd);
		void walking(int k);
		void hearing(char *sentence);
		void speek();
		void writing();
		void ShowMe();
};
// 构造函数定义
person::person()
{
	name=new char[strlen("XXXXXX")+1];
	strcpy(name,"XXXXXX");
	strcpy(pid,"XXXXXXXXXXXXXXXXXX");
	sex='X';
	weight=0;
	high=0;
	year=1900;
	month=day=1;
	hour=miniter=second=0;
}
person::person(char *n,char s,char *p,int w,int hh,int hr,int mr,int sd)
{
	change_data(n,s,p,w,hh,hr,mr,sd);
}
// 修改数据函数定义
void person::change_data(char *n,char s,char *p,int w,int hh,int hr,int mr,int sd)
{
	name=new char[strlen(n)];
	strcpy(name,n);
	strcpy(pid,p);
	sex=s;
	weight=w;
	high=hh;
	char temp[5]; // 通过身份证号码产生出身日期
	strncpy(temp,p+6,4);
	year=atoi(temp);
	strncpy(temp,p+10,2);
	temp[2]='\0';
	month=atoi(temp);
	strncpy(temp,p+12,2);
	temp[2]='\0';
	day=atoi(temp);
	hour=hr;
	miniter=mr;
	second=sd;
}
// 主函数定义
int main()
{
	// 创建对象
	person Jack("James Chen",'M',"610103198901062493",160,180,23,34,35);
	Jack.print(); // 输出人的属性值
	system("pause");
	Jack.walking(10,4); // 行走 10 步,1/4 秒走一步
	Jack.hearing("You are simple"); // 听英文句子
	Jack.speek(1006); // 说出整数 num 的英文句子
	cout<<endl;
	//Jack.writing (); // 书写汉字 “曲”
	return 0;
}

# 三种继承方式

# public 公有继承方式

派生类对基类各种成员访问权限如下:

  • 基类公有成员相当于派生类的公有成员,即派生类可以像访问自身公有成员一样访问从基类继承的公有成员
  • 基类保护成员相当于派生类的保护成员,即派生类可以像访问自身的保护成员一样,访问基类的保护成员
  • 派生类内部成员无法直接访问基类的私有成员
公有继承举例
#include <iostream>
#include <cstring>
using namespace std;
// 人员类定义
class Person 
{
	protected:
		char Name[10]; // 姓名
		int Age; // 年龄
		char Sex; // 性别
	public:
		void Register(char *name, int age, char sex) // 设置数据成员
		{
			strcpy(Name, name);
			Age = age;
			Sex=(sex=='m'? 'm':'f');
		}
		void ShowMe() // 输出数据成员
		{
			cout<<Name<<"\t"<<Sex<<"\t"<<Age<<"\t";
		}
};
// 雇员类定义
class Employee: public Person 
{
		char Dept[20]; // 工作部门
		float Salary; // 月薪
	public:
		Employee()
		{
			EmployeeRegister("XXX",0,'m',"XXX",0);
		}
		void EmployeeRegister(char *name, int age, char sex, char *dept, float salary);
		void ShowEmp(); // 显示雇员信息
};
void Employee::EmployeeRegister(char *name, int age, char sex, char *dept, float salary)
{
	Register(name,age,sex);
	// 如果改成直接操作基类数据成员?
	strcpy(Dept, dept);
	Salary = salary;
}
void Employee::ShowEmp()
{
	cout<<Name<<"\t"<<Sex<<"\t"<<Age<<"\t";
	// 如果将基类 protected 改为 private?
	cout<<Dept<<"\t"<<Salary;
}
int main()// 主函数
{
	Employee emp;
	emp.EmployeeRegister("张弓长",40,'f',"图书馆",2000);
	emp.ShowEmp();
	// 张弓长  f       40      图书馆  2000
	cout<<endl;
	emp.ShowMe();
	// 张弓长  f       40
	cout<<endl;
	return 0;
}

# private 私有继承方式

派生类对基类各种成员访问权限如下:

  • 基类公有成员和保护成员都相当于派生类的私有成员,派生类只能通过自身的函数成员访问他们
  • 对于基类的私有成员,无论派生类内部成员或派生类使用者都无法直接访问。
将上述例子改为私有继承
...
// 雇员类定义
class Employee: private Person 
...
int main()// 主函数
{
	Employee emp;
	emp.EmployeeRegister("张弓长",40,'f',"图书馆",2000);
	emp.ShowEmp();
	// emp.ShowMe();
	// 本句违反继承规则
	// Showme () 是派生类的私有成员,只能成员函数访问,对象不能访问
	// 报错: 'Person' is not an accessible base of 'Employee'
	return 0;
}

# protected 保护继承方式

派生类对基类各种成员访问权限如下 :

  • 基类的公有成员和保护成员都相当于派生类的保护成员,派生类可以通过自身的成员函数或其子类的成员函数访问他们
  • 对于基类的私有成员,无论派生类内部成员或派生类使用者都无法直接访问
派生类学生类保护继承定义
class Student : protected Person
{
	protected:
		int Number;
		char ClassName[10];
	public:
		void Register(char *classname, int number, char *name, int age, char sex)
		{
			strcpy(ClassName, classname);
			Number = number;
			strcpy(Name, name); // 正确,引用基类的保护成员
			Age = age; // 正确,引用基类的保护成员
			Sex = (sex == 'm'?'m':'f'); // 正确,引用基类的保护成员
		}
		void ShowStu()
		{
			cout << Number << '\t' << ClassName << '\t';
			ShowMe();
		}
};
int main()
{
	Student stu;
	stu.Register("计算机51",85071011,"张弓长",18,'m');
	stu.ShowStu();
	// stu.ShowMe();
	// 错误,对象不能访问保护成员
	return 0;
}

# 小结

派生方式基类中的访问限定在派生类中对基类成员的访问限定外部函数 如 main()
公有继承publicpublic可以直接访问
protectedprotected不可以直接访问
private不可以直接访问不可以直接访问
私有继承publicprivate不可以直接访问
protectedprivate不可以直接访问
private不可以直接访问不可以直接访问
保护继承publicprotected不可以直接访问
protectedprotected不可以直接访问
private不可以直接访问不可以直接访问

# 派生类构造和析构函数

基类的构造函数与析构函数不能被继承

派生类构造函数的一般形式为:

<派生类名>::<派生类名>(<参数总表>): <基类名1>(<参数表1>),...,<基类名n>(<参数表n>),<内嵌对象名1>(<对象参数表1>),...,<内嵌对象名m>(<对象参数表m>)
{
	<派生类新增加成员的初始化>;
}

# 执行顺序

#include<iostream>
#include<string.h>
using namespace std;
class Person
{
		char Name[10]; // 姓名
		int Age; // 年龄
	public:
		Person(char* name,int age)
		{
			strcpy(Name, name);
			Age = age;
			cout<<"constructor of person"<<Name<<endl;
		}
		~Person() {
			cout<<"deconstrutor of person"<<Name<<endl;
		}
};
class Employee: public Person
{
		char Dept[20];
		Person Leader; // 内嵌对象
	public:
		Employee(char *name, int age, char *dept, char *name1, int age1):Person(name,age), Leader(name1,age1)
		{
			strcpy(Dept, dept);
			cout<<"constructor of Employee"<<endl;
		}
		~Employee() {
			cout<<"deconstrucor of Employee"<<endl;
		}
};
int main()
{
	Employee emp("张弓长",40,"人事处","李木子",36);
	return 0;
}
//constructor of person 张弓长
//constructor of person 李木子
// constructor of Employee
// deconstrucor of Employee
//deconstrutor of person 李木子
//deconstrutor of person 张弓长
  • 构造函数的执行顺序
    首先,调用基类构造函数,调用顺序按照它们被继承时声明的基类名顺序执行。
    其次,调用内嵌对象构造函数,调用次序按各个对象在派生类内声明的顺序。
    最后,执行派生类构造函数体中的内容。

  • 析构函数的执行顺序
    派生类析构函数执行过程恰与构造函数执行过程相反。
    首先,执行派生类析构函数。
    然后,执行内嵌对象的析构函数。
    最后,执行基类析构函数。

# 课堂讨论

  1. 对于 person 来说,Date 类和 Time 类作为基类合适,还是其对象作为 person 内嵌对象(即作为 person 的数据成员)合适?请说说理由。

其对象作为 person 内嵌对象(即作为 person 的数据成员)合适。
理由:
一个合理的派生,应该是:一个派生类对象 一定是 一个基类对象,而 Date 类和 Time 类和 person 类无直接关系,所以作为 person 内嵌对象比较合适。

  1. 构造函数可以继承吗?如果能,怎样初始化对象;如果不能,说说理由。

不能,构造函数是用来初始化类的对象,与父类的其他成员不同,他不能被子类继承(子类可以继承父类所有的成员变量和成员方法,但是不继承父类的构造方法)。在创建子类对象时,为了初始化从父类继承来的数据成员,系统需要调用其父类的构造方法。

  1. 基类中的保护成员和私有成员被派生类公有继承后,在访问属性上有什么区别?(类内的访问限制和类外的访问限制)

基类中的保护成员被派生类公有继承后,在派生类内相当于保护成员,可以在派生类内直接访问、在类外不能直接访问;
基类中的私有成员被派生类公有继承后,在派生类内不能直接访问,在类外也不能被直接访问。

# 随堂练习

  1. 视频中 person 类的析构函数体里的 delete 语句可以省略。

  2. 下列叙述正确的是

    • 基类是一类特殊定义的类
    • 派生类只能从一个基类继承
    • 派生类中必须定义构造函数
    • 派生类的成员函数可以和基类的成员函数同名
  3. 在派生类的类体中,只能定义新增的数据成员和新增的函数成员。

  4. 可以在类外用 a.x 的形式访问派生类对象 a 的基类成员 x ,其中 x

    • 私有继承的公用成员
    • 公用继承的私有成员
    • 公用继承的保护成员
    • 公用继承的公用成员
  5. 在派生类的定义中,无论采用三种继承方式任何一种,都无法直接访问基类中的私有成员。

阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Ruri Shimotsuki 微信支付

微信支付

Ruri Shimotsuki 支付宝

支付宝

Ruri Shimotsuki 贝宝

贝宝