继承与派生
1、继承与派生的定义
继承:保持原有类的特性而构建新类的过程
派生:在已有类的基础上新增自己的特性而产生新类的过程。
(其中,被继承的已有类称为基类,派生出来的新类称为派生类)
2、继承与派生的目的
继承:实现代码重用
派生:当新的问题出现时,原有程序无法解决或不能完全解决时,需要对原有程序进行改造
3、派生类的声明
class 派生类名:继承方式 基类名
{
……
}
4、三种继承方式
A.公有继承
B.私有继承
C.保护继承
(不同继承方式的区别主要体现在:
——派生类成员对基类成员的访问权限
——派生类对象对基类成员的访问权限)
A.公有继承(public)
&&基类的public和protected成员的访问属性在派生类中保持不变,但基类中的private成员不可直接访问
&&派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员
&&通过派生类的对象只能访问基类的public成员
//公有继承实例
//派生类访问公有继承的基类成员
#include<iostream>
#include<string>
using namespace std;
class Student
{
private:
int num;
string name;
string sex;
public:
void get_value()//声明一个输入函数
{
cin>>num>>name>>sex;
}
void display()//声明一个输出函数
{
cout<<"num: "<<num<<endl;
cout<<"name: "<<name<<endl;
cout<<"sex: "<<sex<<endl;
}
};
class Student1:public Student//公有继承
{
private:
int age;
string addr;
public:
void get_value_1()//声明一个输入函数
{
get_value();//通过基类的公有成员函数get_value()访问基类的私有成员
cin>>age>>addr;
}
void display_1()//声明一个输出函数
{
display();//通过基类的公有成员函数display()访问基类的私有成员
cout<<"age:"<<age<<endl;
cout<<"address:"<<addr<<endl;
}
};
/*由于基类的私有成员对派生类说是不能访问的,所以派生类的
成员函数display_1不能直接访问基类的私有成员。只能通过基类
的公有成员函数访问基类的私有成员。*/
int main()
{
Student1 stud1;
stud1.get_value_1();
stud1.display_1();
return 0;
}
B.私有继承(private)
&&基类的public和protected成员都已private身份出现在派生类中(降级),但基类中的private成员不可直接访问
&&派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员;在派生类的外部,派生类的对象无法访问基类的全部成员。
&&通过派生类的对象不能直接访问基类的任何成员(全部基类成员在派生类中都成为了私有成员,无法进一步派生。私有继承方式一般很少使用)
//私有继承实例
//与上述公有继承相似,只需做如下变化,其他相同部分省略
class Student1:private Student//私有继承
C.保护继承(protexted)
&&基类的public和protected成员都已protected身份出现在派生类中(降级),但是基类的private成员不可直接访问(但保护继承可以进一步派生,而私有继承则不可以)
&&派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员
&&通过派生类的对象不能直接访问基类的任何成员
5、类型兼容规则
A派生类的指针可以隐含转换为基类的指针.
B派生类的对象可以隐含转换为基类对象
C派生类对象可以初始化基类的引用
#include <iostream>
using namespace std;
class B0 // 基类B0声明
{ public: // 公有成员函数
void display(){cout<<"B0::display()"<<endl;}
};
class B1: public B0
{ public:
void display(){cout<<"B1::display()"<<endl; }
};
class D1: public B1
{ public:
void display(){cout<<"D1::display()"<<endl;}
};
void fun(B0 *ptr)
{
ptr->display(); //"对象指针->成员名"
}
int main() //主函数
{ B0 b0; //声明B0类对象
B1 b1; //声明B1类对象
D1 d1; //声明D1类对象
B0 *p; //声明B0类指针
p=&b0; //B0类指针指向B0类对象
fun(p);
p=&b1; //B0类指针指向B1类对象
fun(p);
p=&d1; //B0类指针指向D1类对象
fun(p);
return 0;
}
运行结果为:B0::display()
B0::display()
B0::display()
6、派生类的构造函数
声明方法:
派生类名::派生类名(基类所需的形参,本类成员所需的形参):基类名(参数表)
{
本类成员初始化赋值语句;
};
Tips:
&&基类的构造函数不被继承,派生类中需要声明自己的构造函数,派生类的构造函数需要给基类的构造函数传递参数
&&定义构造函数时,只需要对本类中新增成员进行初始化,对继承来的基类成员的初始化可自动调用基类构造函数完成
//在建立一个对象时,执行构造函数的顺序是:①派生类构造函数先调用基类构造函数;②再执行派生类构造函数本身(即派生类构造函数的函数体),用派生类构造函数的形参做基类构造函数的实参
#include<iostream>
#include <string>
using namespace std;
class Student //声明基类
{
public:
Student(int n,string nam,char s) //基类构造函数
{
num=n;
name=nam;
sex=s;
}
~Student( ) { } //基类析构函数(内容为空)
protected:
int num;
string name;
char sex ;
};
class Student1: public Student // 声明公用派生类
{
public:
Student1(int n,string nam,char s,int a,char ad[ ] ): Student (n,nam,s) // 派生类构造函数
{
age=a; // 对派生类新增的数据成员初始化
addr=ad;
}
void show( )
{
cout<<"num: "<<num<<endl;
cout<<"name: "<<name<<endl;
cout<<"sex: "<<sex<<endl;
cout<<"age: "<<age<<endl;
cout<<"address: "<<addr<<endl;
}
private: // 派生类的私有部分
int age;
string addr;
};
int main( )
{
Student1 stud1(10010,"Wang-li",'f',19,"115 Beijing Road,Shanghai");
Student1 stud2(10011,"Zhang-fun",'m',21,"213 Shanghai Road,Beijing");
stud1.show( ); // 输出第一个学生的数据
stud2.show( ); // 输出第二个学生的数据
return 0;
}
7、同名隐藏规则
&&若未强行指明,则通过派生类对象使用的是派生类的同名成员。(基类中的同名成员函数会被隐藏)
&&若要通过派生类对象访问基类中被隐藏的同名成员,应该使用基类名限定
8、二义性的问题
产生:当派生类从多个基类派生,而这些基类有从同一个基类派生,则在访问此共同基类中的成员时,将产生二义性。
解决方法:
A.用类名来限定 eg.c1.A::f()或c1.b::f()
B.同名隐藏,即在派生类中声明一个同名成员函数
C.虚基类(最标准的方法)
( 虚基类的声明
class 派生类名:virtual 继承方式 基类名)
在第一级继承时,就要将共同基类设置为虚基类