派生与继承的基本概念
一个基类可以派生出多个派生类,每个派生类又可以作为基类再派生出新的派生类。
一个派生类只从一个基类派生,称作单继承。【基本类似于数据结构中的树的概念】
一个派生类也可以有两个或多个基类,称作多重继承
定义与声明
派生类的声明格式为——
class 派生类名 : [继承方式] 基类名
{
派生类新增成员声明
}
此外注意:
- 派生类从基类接收成员,但是不包括基类的构造函数和析构函数。
因此,派生类需要自己定义构造函数。 - 派生类可以定义自己的(基类以外)的成员和函数,同时可以屏蔽原来从基类继承过来的。
公用继承
派生类成员无法直接访问基类的私有成员,其它基类的成员不变得搬到派生类中.
基类的私有数据成员在派生类中是存在的,但不可直接访问,需要用从基类继承下来的函数访问。
私有继承(私用继承)
- 私用继承中 基类全部成员在派生类中都成为了私有,派生类的下一步派生无法使用这些成员。
- 基类中公有和保护成员是派生类的私有成员,但派生类可以直接访问这些成员。而基类的私有成员还是不能被直接访问
保护继承
- 基类中公有和保护成员是派生类的保护成员,派生类可以直接访问这些成员。而基类的私有成员还是不能被直接访问。【和私有继承一致】
- 保护继承中 基类全部成员在派生类中都成为了保护,派生类的下一步派生依然能够使用这些成员。【与私有继承不同】
总结
访问级别:公用——类内、类外都可以访问;保护——类内、类外都可以访问;私有——仅类内可访问;不可访问——谁都不行



简单派生类的构造函数
- 方法一:
派生类构造函数名(基类成员初始化参数,派生类成员初始化参数) : 基类构
造函数名(基类参数){派生类成员初始化赋值语句;} - 方法二:
派生类构造函数名(基类成员初始化参数,派生类成员初始化参数) : 基类构
造函数名(基类参数), 初始化表 {}
// 基类构造函数
Student(int n, string nam, string s ) {
num=n;
name=nam;
sex=s; }
//派生类构造函数
Student1( int n, string nam, string s, int a, string ad)
: Student ( n, nam, s)
{ // 只对派生类新增的数据成员初始化
age=a;
addr=ad;}
构造与析构的顺序
类派生时,派生类不能继承基类的析构函数
先调用的后析构——
派生类构造函数先调用基类构造函数;再执行派生类构造函数本身(即派生类构造函数的函数体)
释放派生类对象时,先执行派生类析构函数,再执行其基类的析构函数
有子对象的派生类的构造函数
class Student { //声明基类
public: //公用部分
Student(int n,string nam ) { //基类构造函数
num=n;
name=nam;
}
void display() {
cout<<"学号:"<<num<<endl<<"姓名:"<<name<<endl;
}
protected: //保护部分
int num;
string name;
char sex ;
};
class Student1: public Student // public方式派生
{
private: // 派生类的私有数据
Student monitor; // 定义子对象(班长)
int age;
string addr;
public:
//下面是派生类构造函数
Student1(int n,string nam,int n1,string nam1,int
a,string ad): Student(n,nam), monitor(n1,nam1)
{
age=a; // 在此处只对派生类新增的数据成员初始化
addr=ad;
}
void show( ) {
cout<<"这个学生是:"<<endl;
display();
// 输出num和name
cout<<"年龄: "<<age<<endl;
cout<<"地址: "<<addr<<endl<<endl;
}
// 输出子对象的数据成员
void show_monitor()
{
cout<<endl<<"班长是:"<<endl;
monitor.display(); // 调用成员函数
}
};
int main( )
{
Student1 stud1(10010, "王力", 10001, "李军", 19, "上海市北京路115号");
stud1.show( ); // 输出第一个学生的数据
stud1.show_monitor(); // 输出子对象的数据
return 0;
多级继承

多重继承
多重继承指有多个继承类共有一个基类。
class B1 { //基类B1,构造函数有参数
public:
B1(int i) { cout<<"构造 B1 "<<i<<endl; }
};
class B2 { //基类B2,构造函数有参数
public:
B2(int j) { cout<<"构造 B2 "<<j<<endl; }
};
class B3 { //基类B3,构造函数无参数
public:
B3() { cout<< "构造 B3 *"<<endl; }}
class C : public B2, public B1, public B3{
public: // 派生类的公有成员
C(int a, int b, int c, int d) :
B1(a), B2(b), memberB1(c), memberB2(d) {}
private: // 派生类的私有对象成员
B1 memberB1;
B2 memberB2;
B3 member B3;
}

可以看到第一轮 按照声明派生类时基类对应的顺序 2 1 3 调用构造函数
然后依次读取参数 a b c d 并构造
与多级继承不同是:
多级继承 是B继承A,C继承B
多重继承的构造
多重继承时,系统先调用基类的构造函数,再调用子对象的构造函数,最后调用派生类的构造函数。
多重继承的二义性问题
是派生类继承多个基类同名成员而产生的二义性
解决:在派生类对象名后增加直接基类名。
就是在main 函数里写为——(加上全局范围符:: 则其类得到限定)
c1. A::a = 3;
c1. A::display();
虚基类
有共同基类的场合,当基类通过多条派生路径被一个派生类继承时,派生类只继承该基类一次。
虚基类是一个虚基类和一堆直接基类、一堆继承类的游戏——他们有许多相同的参数 有少数不同的参数
声明方式:以virtual修饰说明基类
class 派生类名 : virtual 继承方式 基类名 { }
注意
一个基类可以作为一个派生类的虚基类同时也可以作为另一个派生类的非虚基类。

虚基类初始化
最后的派生类不仅要负责对其直接基类初始化,还要负责对虚基类初始化
也就是上图的D类不仅要对直接基类(B、C)初始化,还要对虚基类(A)初始化
#include <iostream>
#include <string>
using namespace std;
class Person{
public:
Person(string nam, char s, int a)
{name = nam; sex=s; age=a; }
protected: // 保护成员
string name;
char sex;
int age;
};
// 声明Person为公用继承的虚基类
class Teacher : virtual public Person
{public:
Teacher(string nam, char s, int a, string t) : Person(nam, s, a)
{ title = t; } // 构造函数
protected: // 保护成员
string title; // 职称
};
// 声明Person为公用继承的虚基类
class Student : virtual public Person
{public:
Student(string nam, char s, int a, float sco) :
Person(nam,s,a), score(sco) { }
protected: // 保护成员
float score; // 成绩
}
// 声明Teacher和Student类为公用继承的直接基类
class Graduate : public Teacher, public Student
{
public:
Graduate(string nam,char s,int a, string t,float sco,float w) : // 构造函数
Person(nam,s,a), Teacher(nam,s,a,t), Student(nam,s,a,sco), wage(w) { }
基类与派生类的转化
- 派生类的对象可以被赋值给基类对象。
- 派生类的对象可以初始化基类的引用。
- 指向基类的指针也可以指向派生类。
- 通过基类对象名、指针只能使用从基类继承的成员
由于派生类完全包含了基类,因此可以将派生类对象用基类对象表示,用来操作派生类对象里的基类对象。
但是基类的对象不能够被当作派生类的对象。
派生与继承详解
925

被折叠的 条评论
为什么被折叠?



