C++系列:继承和派生(四)
继承和派生
继承和派生的概念
- 继承:在定义以恶搞新的类B时,如果该类与某个已有的类A相似(指的是B拥有A的全部特点),那么就可以把A作为一个基类,而把B作为基类的一个派生类(也称子类)。
- 派生类是通过对基类进行修改和扩充得到的。在派生类中,可以扩充新的成员变量和成员函数。
- 派生类一经定义后,可以独立适用,不依赖于基类。
- 派生类拥有基类的全部成员函数和成员变量,不论是private、protected、public。
- 在派生类的各个成员函数中,不能访问基类中的private成员。
需要继承的例子
派生类的写法
//语法
class 派生类名 :public 基类名
{
};
//实例
class CStudent
{
private:
string sName;
int nAge;
public:
bool IsThreeGood(){};
void SetName(const string & name){sName = name;}
}
class CUndergraduateStudent:public CStudent{
//新的派生类包含基类的成员变量和函数
private:
int nDepartment; //新增的成员变量
public:
bool IsThreeGood(){} //覆盖,相同的成员函数名就会覆盖基类中的
bool CanBaoYan(){};
};
class CGraduatedStudent:public CStudent{
private:
int nDepartment;
char szMentorName[20];
public:
int CountSalary(){};
};
派生类对象的内存空间
- 派生类对象的体积,等于基类对象的体积,再加上派生类对象自己的成员变量的体积。在派生类对象中,包含着基类对象,而且基类对象的存储位置位于类派生类对象新增的成员变量之前。
class CBase
{
int v1,v2;
};
class CDerived:public CBase
{
int v3;
};
- 派生类对象包含基类对象,派生类对象新增的内容位于基类对象之后
//实例
#include
#include
using namespace std;
class CStudent
{
private:
string name;
string id;
char gender;
int age;
public:
void Printlnfo();
void Setlnfo(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 printlnfo()
{
CStudent::Printlnfo(); //调用基类的Printlnfo
cout << “Department:” << department << endl;
}
void Setlnfo(const string &name_, const string &id_, int age_, char gender_, const string &department_)
{
CStudent::Setlnfo(name_, id_, age_, gender_); //调用基类的Setinfo
department = department_;
}
};
int main()
{
CUndergraduateStudent s2;
s2.Setlnfo(“Harray Potter”, “118829212”, 19, ‘M’, “Computer Science”);
cout << s2.GetName() << “”;
s2.QualifiedForBaoyan();
s2.printlnfo();
return 0;
}
继承关系和复合关系
继承 “是”关系
- 满足逻辑要求“ 一个B对象也是一个C对象”
- 基类A,B是基类A的派生类
复合 “有”关系
- 类C中“有”成员变量k,k是类D的对象,则C和D是复合关系
- 一般逻辑上要求:“D对象是C对象的固有属性或组成部分”
继承关系使用
- 人 对男人和女人 的关系,把人作为基类,男人和女人作为人的派生类
复合关系使用
需要“点”类,也需要“圆”类时,两者的关系就是复合关系,即每一个圆包含一个点
//正确做法
class CPoint
{
double x,y;
friend class CCircle;
//便于Ccirle类操作其圆心
};class CCircle
{
double r;
CPoint center;
};小区业主与狗的关系
//这样的定义是错的,循环定义了
class CDog;
class CMaster
{
CDog dogs[10];
};class CDog
{
CMaster m;
};//改进
class CDog;
class CMaster
{
CDog * dogs[10]; //CDog类的指针数组
};
class CDog{
CMaster m;
};
//依然不行 在调用修改狗的主人的信息时,需要维持不同狗之间的一致性,比较繁琐//再次改进
class CMaster; //先声明
class CDog{
CMaster *pm;
};
class CMaster{
//这样使得狗对象只能通过人来调用,失去了自由
CDog dogs[10];
};//正确做法
class CMsater;class CDog{
CMaster *pm;
};
class CMaster{
CDog *dogs[10]; //这种关系也被称为”知道”
};
派生类覆盖基类成员
派生类可以定义一个和基类成员同名的成员,这叫做覆盖。== 在派生类中访问这类成员时,缺省的情况是访问派生类中定义的成员。要在派生类中访问由类定义的同名成员时,要使用作用域符号 ::
class base//基类
{
int j;
public:
int i;
void func();
};class derived:public base//派生类
{
public:
int i;
void access();
void func();
};void derived::access() //派生类的成员函数
{
j=5; //这里是在派生类里找,错误error
i=5; //派生类i
base::i =5; //基类i
func();//派生类fun
base::func();//基类fun
}//注意
一般来说,基类和派生类不定义同名的成员变量
//占用空间
| base::j |
| base::i |
| i |
类的保护成员
private: //基类的成员函数、友元函数可以访问
public: //基本上都可以访问
protected : //基类的成员函数、友元函数、派生类的成员函数可以访问当前对象的基类的保护成员
class Father{
private: int nPrivate;
public: int nPublic;
protected: int nProtected;
};
class Son: public Father{
void AccessFatther()
{
nPublic = 1;// ok
nPrivate = 1 ; //wrong 访问了基类的私有成员
nProtected = 1; // ok ,派生类可以访问基类的保护成员
Son f;
f.nProtected = 1; //wrong ,f不是当前对象
}
};
int main()
{
Father f;
Son s;
f.nPublic = 1;// OK
s.nPublic = 1; //OK
f.nProtected = 1; //error 保护
f.nPrivate = 1; //error 私有
s.nProtected = 1;//error 保护
s.nPrivate = 1; //error 私有
return 0;
}
派生类的构造函数
#include
using namespace std;
class Bug //基类
{
private:
int nLegs;
int nColor;
public:
int nType; //公有
Bug(int legs, 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)//派生类成员函数
{
nLegs = legs; //访问的是基类的私有成员数据 错误
nColor = color; //访问的是基类的私有成员数据 错误
nType = 1; //访问基类的公有成员
nWings = wings; //自己的私有成员
}
//正确 的派生类构造函数
FlyBug::FlyBug(int legs,int color,int wings):Bug(legs,color)
{
nWings = wings;
}
FlyBug::FlyBug(int legs, int color, int wings) : Bug(legs, color)
{
nWings = wings;
}
int main()
{
FlyBug fb(2, 3, 4);
fb.nType = 1;
fb.nLegs = 2; //这里同样不能调用基类的私有成员 错误
return 0;
}
//在一个派生类的构造函数被执行之前,总是先执行基类的构造函数
//相反,在派生类的析构函数被执行时,执行完派生类的析构函数后,自动调用基类的构造函数
调用基类构造函数的两种方式
显示方式:在派生类的构造函数中,为基类的构造函数提供参数
derived::derived(arg_derived-list):base(arg_base-list)
隐式方式:在派生类的构造函数中,省略基类构造函数时,派生类的构造函数则自动调用基类的默认构造函数。
包含成员对象的派生类的构造函数写法
class Bug{
private:
int nLegs;
int nColor;
public:
int nType;
Bug(int legs,int color);
void PrintBug(){};
};
class SKill
{
public:
Skill(int n){}
};
class FlyBug:public Bug{
int nWings;
Skill sk1,sk2;
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 继承的赋值兼容规则
class base{};
class derived:public base{};
base b;
derived d;
派生类对象可以赋值给基类对象
b = d;
派生类对象可以初始化基类引用
base & br = d;
派生类对象的地址可以赋值给基类指针
base * pb = & d;
直接基类与间接基类
A-派生-B-派生-C
A是B的直接基类,A是C的间接基类
- 声明派生类时,只需要列出它的直接基类
- 派生类沿着类的层次自动向上继承它的间接基类
- 派生类的成员包括
- 派生类自己定义的成员
- 直接基类中的所有成员
- 所有间接基类的全部成员
#include
using namespace std;
class Base{
public:
int n;
Base(int i):n(i)
{
cout << “Base” << n << “constructed” << endl;
}
~Base(){
cout << “Base” << n << “dextructed” << endl;
}
};
class Derived:public Base
{
public:
Derived(int i):Base(i){
cout << “Derived costructed” << endl;
}
~Derived()
{
cout << “Derived dsetucted” << endl;
}
};
class MoreDerived:public Derived
{
public:
MoreDerived():Derived(4) //只用写明直接基类的成员赋值就行,不需要写间接基类
{
cout << “More Derived constructed” << endl;
}
~MoreDerived()
{
cout << “More Derived destructed” << endl;
}
};
int main()
{
MoreDerived Obj; //从最顶层基类开始构造
return 0;
} //先析构最后派生的类
