继承
继承定义
格式:
class 父类{};
class 子类:继承方式 父类名{
//新增子类数据
};
其中继承方式有:private protected public(推荐)
**public:**可以被任意实体访问
**protected:**只允许子类及本类的成员函数访问
**private:**只允许本类的成员函数访问
公共继承 保持不变,保护继承变保护,私有继承变私有,所有父类私有在子类中不可见。
class Person{
public:
int a;
protected:
int b;
private:
int c;
};
class Son:public Person{
public:
//在子类的成员函数中,protected是可以访问的
void fun(){
cout<<a<<b;//正常访问
cout<<c;// error c不可访问
}
};
int main()
{
Son ob;//子类的对象
cout<<ob.a<<endl;
cout<<ob.b<<endl;//b是protected,不可通过对象访问
return 0;
}
class Person{
protected:
int a;
};
int main()
{
Person p;
p.a=8;//error protected只允许本类或子类的成员函数访问
return 0;
}
子类的构造析构顺序
#include <iostream>
using namespace std;
class Person{
public:
Person(){
cout<<"父类构造函数"<<endl;
}
~Person(){
cout<<"父类析构函数"<<endl;
}
};
class Other{
public:
Other(){
cout<<"成员对象构造函数"<<endl;
}
~Other(){
cout<<"成员对象析构函数"<<endl;
}
};
class Son:public Person{
public:
Other ob;
public:
Son(){
cout<<"子类构造函数"<<endl;
}
~Son(){
cout<<"子类析构函数"<<endl;
}
};
int main()
{
Son ob;
return 0;
}
打印结果:
父类构造函数
成员对象构造函数
子类构造函数
子类析构函数
成员对象析构函数
父类析构函数
子类调用成员对象和父类的有参构造
-
子类 会自动调用成员对象、父类的默认构造
-
子类 必须使用初始化列表调用成员对象、父类的有参构造。
初始化列表时:父类写类名称 成员对象用对象名。
#include <iostream>
using namespace std;
class Person{
public:
int a;
public:
Person(){
cout<<"父类构造函数"<<endl;
}
Person(int a){
this->a=a;
cout<<"父类的有参构造"<<endl;
}
~Person(){
cout<<"父类析构函数"<<endl;
}
};
class Other{
public:
int b;
public:
Other(){
cout<<"成员对象构造函数"<<endl;
}
Other(int b){
this->b=b;
cout<<"成员对象的有参构造"<<endl;
}
~Other(){
cout<<"成员对象析构函数"<<endl;
}
};
class Son:public Person{
public:
Other ob;
int c;
public:
Son(){
cout<<"子类构造函数"<<endl;
}
Son(int a,int b,int c):Person(a),ob(b){
//this->a=a;//注意:这样只会先调用父类的无参构造后进行赋值
this->c=c;
cout<<"子类的有参构造"<<endl;
}
~Son(){
cout<<"子类析构函数"<<endl;
}
};
int main()
{
Son ob(10,20,30);
return 0;
}
父类的有参构造
成员对象的有参构造
子类的有参构造
子类析构函数
成员对象析构函数
父类析构函数
子类和父类同名成员处理
同名成员 最简单 最安全的处理方式:加作用域
1、子类和父类 同名成员数据
子类默认优先访问 子类的同名成员
必须加父类作用域 访问父类的同名成员
class Person{
public:
int a;//同名a
public:
Person(int a){
this->a=a;
}
};
class Son:public Person{
public:
int a;
public:
Son(int a,int b):Person(a){
this->a=b;
}
};
int main()
{
Son ob(100,200);
cout<<ob.a<<endl;//优先访问子类中的a
cout<<ob.Person::a<<endl;//想访问父类中的a,只能带上作用域
return 0;
}
2、子类和父类同名成员函数
重载:无继承,同一作用域,参数的个数、顺序、类型不同都可重载
重定义:有继承,子类 重定义父类的同名函数(参数可以不同)(非虚函数)
子类一旦重定义了父类的同名函数(不管参数是否一致),子类中都将屏蔽父类所有的同名函数。
#include <iostream>
using namespace std;
class Person{
public:
void fun(){
cout<<"Person的fun()"<<endl;
}
};
class Son:public Person{
public:
void fun(){
cout<<"Son类的fun()"<<endl;
}
void fun(int a){
cout<<"Son类的fun(int)"<<a<<endl;
}
};
int main()
{
Son ob;
ob.fun();//默认调用Son中的fun
ob.fun(10);
ob.Person::fun();//使用父类中fun
return 0;
}
子类不能继承父类的成员
父类的构造、析够、operator= 不能被子类 继承
多继承
1、多继承的格式
class 父类1{};
class 父类2{};
class 子类:继承方式1 父类1, 继承方式2 父类2
{
//新增子类数据
};
子类就拥有了父类1,父类2的大部分数据
2、多继承中的同名函数
#include <iostream>
using namespace std;
class Person1{
public:
int a;
Person1(int a):a(a){}
};
class Person2{
public:
int a;
Person2(int a):a(a){}
};
class Son:public Person1,public Person2{
public:
int a;
Son(int a,int b,int c):Person1(a),Person2(b),a(c){};
};
int main()
{
Son ob(1,2,3);
cout<<ob.a<<endl;//3
cout<<ob.Person1::a<<endl;//1
cout<<ob.Person2::a<<endl;//2
return 0;
}
菱形继承
菱形继承:有公共祖先的继承 叫菱形继承。
最底层的子类 数据 会包含多分(公共祖先的数据)
#include <iostream>
using namespace std;
class People{
public:
int age;
};
class Person1: public People{
};
class Person2: public People{
};
class Son:public Person1,public Person2{
};
int main()
{
Son ob;
//cout << ob.age << endl;//二义性
cout<< ob.Person1::age<<endl;
cout<< ob.Person2::age<<endl;
return 0;
}
怎么才能只要公共祖先的一份数据呢?
在继承方式 前加virtual修饰,子类虚继承父类 子类只会保存一份公共数据。
#include <iostream>
using namespace std;
class People{
public:
int age;
};
class Person1:virtual public People{
};
class Person2: virtual public People{
};
class Son:public Person1,public Person2{
};
int main()
{
Son ob;
cout<<ob.age<<endl;
return 0;
}
虚继承 会在子类中产生 虚基类指针(vbptr) 指向 虚基类表(vbtable), 虚基类表纪录的是通过该指针访问公共祖先的数据的偏移量