概念:
在定义一个新的类B时,和某个已有的类A相似(指B拥有A的全部特点),那么就可以把A作为一个基类,而把B作为基类的一个派生类。(子类)
提高重用性。
派生类是通过基类修改和扩充得到的。
派生类拥有基类全部的成员函数和成员变量。但不能访问基类中private成员。
派生类的写法:
class 派生类名 : public 基类名
{
...
};
Example:学生管理程序。
class CStudent{ //基类,包括所有学生的共性。
private:
string sName;
int nAge;
public:
bool IsThreeGood(){}; //三好学生。
void SetName( const string & name)
{sName = name;}
//.....
};
class CUndergraduateStudent: public CStudent{//本科类,由student派生而来。
private:
int nDepartment; //新的变量,系。department
public:
bool IsThreeGood(){...}; //覆盖
bool CanBaoYan(...){};
}; //派生类写法 类名:public 基类名
class CGraduateStudent:public CStudent{
private:
int nDepartment;
char szMentorName[20];
public:
int CountSalary(){...};
};
派生类对象占用的空间是基类的空间加上自己成员变量的体积。基类对象的存储位置位于派生类对象新增的成员变量之前。
学籍管理系统具体实现:
#include <iostream>
#include <string>
using namespace std;
class CStudent{
private:
string name;
string id;
char gender;
int age;
public:
void PrintInfo();
void SetInfo(const string & name_,const string &id_, int age_, char gender_);
string GetName()
{return name;}
};
class CUndergraduateStudent:public CStudent
{
private:
string department;
public:
void QualifiedForBaoyan(){
cout<< "qualified for baoyan" << endl;
}
void PrintInfo(){
CStudent::PrintInfo(); //调用基类的PrintInfo();
cout<<"Department:" << department<<endl;
}
void SetInfo(const string & name_,const string &id_, int age_, char gender_,const string & department_){
CStudent::SetInfo(name_,id_,age_,gender_);
department = department_;
}
};
int main(){
CUndergraduateStudent s2;
s2.SetInfo("Harry Potter","20193715",19,'M',"E.E.");
cout << s2.GetName() << " ";
s2.QualifiedForBaoyan();
s2.PrintInfo();
return 0;
}
继承关系和复合关系:
继承:“是”的关系。
- B是基类A的派生类。
- 逻辑上要求,一个B对象也“是”一个A对象。
复合:“有”的关系。
- 类C中“有”成员变量k,k是类D的对象,则C和D是复合关系。
- D对象是C对象的固有属性或组成部分。
例一:
class CMaster;
class CDog{
CMaster *pm;
}
class CMaster{
CDog dogs[10];
};
例二:
class CMaster;
class CDog{
CMaster *pm;
}
class CMaster{
CDog *dogs[10];
};
例一中在主人类中定义了狗类数组,不能对狗进行独立操作。
但例二与例一的区别在于它定义了狗的指针数组,就能单独操作狗。
同名成员,protected访问范围说明符:
class base{
int j;
public:
int i;
void func();
};
class derive:public base{
public:
int i;
void access();
void func();
};
拥有相同的成员对象i
和成员函数func();
void derived::access()
{
j = 5; //error
i = 5; //引用的是派生类的i
base::i = 5; //引用的是基类的i
func();
base::func();
}
derived obj;
obj.i = 1; //派生类里的成员变量进行赋值。
obj.base::i = 1; //派生类的基类部分成员变量i进行赋值。
obj占用的存储空间就包括:
- 派生类自身的 i
- 基类的变量 i
- 基类的私有成员 j
但一般来说,不推荐基类和派生类定义相同的成员变量。
基类的private成员:只能被自己的成员函数和友元函数访问。
基类的public成员:可以被基类的成员函数,友元函数,派生类的成员,友元函数,和其他函数。
基类的protected成员:可以被基类的成员函数,基类的友元函数,派生类的成员函数(当前对象的基类)。
class Father{
private: int nPrivate;
public: int nPublic;
protected: int nProtected;
};
class Son:public Father{
void AccessFather(){
nPublic = 1; //可行
nPrivate = 1; //不可以,nPrivate是基类的私有成员。
nProtected = 1; //可以。
Son f;
f.nProtected = 1; //错误,不是当前AccessFather的基类protected成员,是另外定义的了。不能访问。
}
};
如果按照下方式定义。
int main(){
Father f;
Son s;
}
则都不能访问protected成员,因为没有声明派生类的基类。
派生类的构造函数:
派生类对象包含基类对象,执行派生类构造函数之前,要先执行基类的构造函数。
具体形式:
构造函数名(形参表):基类名(基类构造函数实参表){}
如:
class Bug{
private:
int nLegs;
int nColor;
public:
int nType;
Bug (int leges,int color);
void PrintBug(){};
};
class FlyBug:public Bug{
int nWings;
public:
FlyBug(int legs, int color, int wings); //派生类的构造函数
};
Bug::Bug(int legs, int color){
nLegs = legs;
nColor = color;
}
FlyBug::FlyBug(int legs, int color, int wings):Bug(legs, color){
nWings = wings;
} //这种构造方式是正确的,符合上面说的约定。
先执行基类的构造函数。
下面的是错误的FlyBug构造函数:
FlyBug::FlyBug(int legs, int color, int wings){
nLegs = legs;
nColor = color; //不能访问基类的private变量。
nType = 1;
nWings = wings;
}
int main(){
FlyBug fb(2,3,4);
fb.PrintBug(); //可以访问基类的公共成员函数。
fb.nType = 1;
fb.nLegs = 2; error//这个不可以。
return 0;
}
创建派生类的对象时:
- 调用基类的构造函数。先初始化派生类对象中基类继承的成员。
- 在执行一个派生类的构造函数之前,总是执行基类构造函数。
如果基类构造函数省略了,则调用的是基类默认构造函数。
包含成员对象的派生类的构造函数:
如:
class FlyBug:public Bug{
int nWings;
Skill sk1,sk2; //sk1和sk2是skill类的对象。
public:
FlyBug(int legs, int color, int wings);
};
FlyBug::FlyBug(int legs, int color, int wings):
Bug(legs,color),sk1(5),sk2(color){
nWings = wings;
}
public继承的赋值兼容规则:
- 派生类对象可以赋值给基类对象。
Bug a;
FlyBug b;
a = b;
如果反过来, b = a; 是不行的。
- 派生类对象可以初始化基类引用。
Bug & a = b;
- 派生类对象的地址,可以赋值给基类指针。
Bug *a = &b
因为派生类地址最前面本身就是用来存放基类对象空间的。
派生类的派生类的派生类这种情况:
- 声明派生类只需声明它的直接基类。
- 派生类的成员包括:
1 自己定义的成员
2 直接基类中的所有成员。
3 所有间接基类的全部成员。