继承
1.语法与方式:
继承的语法:class 子类:继承方式 父类
继承的3种方式:
- 公共继承
- 保护继承
- 私有继承
#include<iostream>
using namespace std;
class Base1{
public:
int a;
protected:
int b;
private:
int c;
};
class Son1:public Base1{
public:
void func(){
a = 10;//父类中public权限的成员,到子类中依然是public权限
b = 10;//父类中protected权限的成员,到子类中依然是protected权限
//c = 10;//父类中private权限的成员,子类中访问不到unreachable报错
}
};
void test01(){
Son1 s1;
s1.a = 100;
//s1.b = 100;//protected权限在子类继承中可以访问,在类外无法访问
//s1.c = 100;//private权限在子类继承中无法访问,在类外无法访问
}
class Son2:protected Base1{
public:
void func(){
a = 20;//父类中public权限的成员,到子类中变为protected权限
b = 20;//父类中protected权限的成员,到子类中依然是protected权限
//c = 20;//父类中private权限的成员,子类中访问不到unreachable报错
}
};
void test02(){
Son2 s1;
//s1.a = 200;//protected权限在子类继承中可以访问,在类外无法访问
//s1.b = 200;//protected权限在子类继承中可以访问,在类外无法访问
//s1.c = 200;//private权限在子类继承中无法访问,在类外无法访问
}
class Son3:private Base1{
public:
void func(){
a = 30;//父类中public权限的成员,到子类中变为private权限
b = 30;//父类中protected权限的成员,到子类中变为private权限
//c = 30;//父类中private权限的成员,子类中访问不到unreachable报错
}
};
void test03(){
Son3 s1;
//s1.a = 200;//private权限在子类继承中可以访问,在类外无法访问
//s1.b = 200;//private权限在子类继承中可以访问,在类外无法访问
//s1.c = 200;//private权限在子类继承中无法访问,在类外无法访问
}
int main(){
system("pause");
return 0;
}
从父类继承过来的成员,哪些属于子类对象中?
答:父类中所有的非静态成员属性都会被子类继承,父类中private成员属性是被编译器隐藏而无法访问,但也继承下去了。
#include<iostream>
using namespace std;
class Base{
public:
int a;
protected:
int b;
private:
int c;
};
class Son:public Base{
public:
int d;
};
void test01(){
//父类中所有的非静态成员属性都会被子类继承
//父类中private成员属性是被编译器隐藏而无法访问,但也继承下去了
cout << "size of Son: " << sizeof(Son) << endl;
}
int main(){
test01();
system("pause");
return 0;
}
子类全部继承了父类Base的属性(包括私有属性)后的大小为16字节。
2.继承中构造与析构顺序:
子类继承父类后,当创建子类对象时也会调用父类的构造函数,父类与子类构造与析构的先后顺序?
#include<iostream>
using namespace std;
class Base{
public:
Base(){
cout << "Base的构造函数!" << endl;
}
~Base(){
cout << "Base的析构函数!" << endl;
}
};
class Son:public Base{
public:
Son(){
cout << "Son的构造函数!" << endl;
}
~Son(){
cout << "Son的析构函数!" << endl;
}
};
void test01(){
Son s1;
}
int main(){
test01();
system("pause");
return 0;
}
总结:在继承中先调用父类构造函数,再调用子类的构造函数,析构顺序与构造相反。
3.继承同名成员处理方式:
当子类与父类出现同名的成员,如何通过子类对象,访问到子类 or 父类中同名的数据?
- 访问子类同名成员,直接访问即可
- 访问父类同名成员,需要添加作用域
#include<iostream>
using namespace std;
class Base{
public:
Base(){
a = 100;
}
void func(){
cout << "Base作用域下的func()函数调用" << endl;
}
int a;
};
class Son:public Base{
public:
Son(){
a = 200;
}
void func(){
cout << "Son作用域下的func()函数调用" << endl;
}
int a;
};
//1.同名成员属性
void test01(){
Son s;
cout << "s.a = " << s.a << endl;
cout << "s.Son::a = " << s.Son::a << endl;
cout << "s.Base::a = " << s.Base::a << endl;
}
//2.同名成员函数
void test02(){
Son s;
s.func();
s.Son::func();
s.Base::func();
}
int main(){
test01();
test02();
system("pause");
return 0;
}
注意:如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有的同名成员函数。
4.继承同名静态成员处理方式:
继承中同名的静态成员在子类对象上如何进行访问?静态成员和非静态成员出现同名,处理方式一致:
- 访问子类同名成员,直接访问即可
- 访问父类同名成员,需要添加作用域
#include<iostream>
using namespace std;
class Base{
public:
static int a;
static void func(){
cout << "Base下的func()函数调用" << endl;
}
};
int Base::a = 100;
class Son:public Base{
public:
static int a;
static void func(){
cout << "Son下的func()函数调用" << endl;
}
};
int Son::a = 200;
//1.同名静态成员属性
void test01(){
cout << "(1)通过对象访问静态成员属性" << endl;
Son s;
cout << "s.a = " << s.a << endl;
cout << "s.Son::a = " << s.Son::a << endl;
cout << "s.Base::a = " << s.Base::a << endl;
cout << "(2)通过类名访问静态成员属性" << endl;
cout << "Son作用域下的成员属性a = " << Son::a << endl;
cout << "Base作用域下的成员属性a = " << Son::Base::a << endl;
//第一个::代表通过类名的方式访问、第二个::代表访问父类作用域下
}
//2.同名静态成员函数
void test02(){
cout << "(1)通过对象访问静态成员函数" << endl;
Son s;
s.func();
s.Base::func();
cout << "(2)通过类名访问静态成员函数" << endl;
Son::func();
Son::Base::func();
}
int main(){
test01();
test02();
system("pause");
return 0;
}
总结:同名静态成员处理方式和非静态处理方式一样,不同之处在于有两种访问类内成员属性/函数的方式(通过对象&类名)。
5.多继承语法:
cpp中允许一个类继承多个类
语法:class 子类:继承方式 父类1, 继承方式 父类2...
多继承中可能引发父类中有同名成员出现,需要添加作用域进行区分。
注意:cpp实际开发中不建议使用多继承
#include<iostream>
using namespace std;
class Base1{
public:
Base1(){
a = 100;
b = 100;
}
int a;
int b;
};
class Base2{
public:
Base2(){
a = 200;
b = 200;
}
int a;
int b;
};
//子类需要继承Base1与Base2
class Son:public Base1, public Base2{
public:
Son(){
c = 300;
d = 400;
}
int c;
int d;
};
void test01(){
Son s;
cout << "sizeof Son = " << sizeof(s) << endl;
//当父类中出现同名成员时,需要加作用域进行区分
cout << "Base1::a = " << s.Base1::a << endl;
cout << "Base2::a = " << s.Base2::a << endl;
}
int main(){
test01();
system("pause");
return 0;
}
6.菱形继承(虚继承):
两个派生类继承同一个Base基类,又有某个类同时继承着这两个派生类,这种继承方式被称为菱形继承(砖石继承)。
#include<iostream>
using namespace std;
class Animal{
public:
int age;
};
class Tiger:public Animal{};
class Rabbit:public Animal{};
class Tibbit:public Tiger, public Rabbit{};
void test01(){
Tibbit tibbit;
//菱形继承时,两个父类拥有相同的数据,需要加上作用域进行区分
tibbit.Tiger::age = 18;
tibbit.Rabbit::age = 28;
cout << "tibbit.Tiger::age" << tibbit.Tiger::age << endl;
cout << "tibbit.Rabbit::age" << tibbit.Rabbit::age << endl;
//这份数据我们知道,只要有一份即可(菱形继承导致数据冗余)
}
int main(){
test01();
system("pause");
return 0;
}
菱形继承导致数据重复冗余、浪费资源,继承基类Base时添加virtual
关键字利用虚继承可以解决:
#include<iostream>
using namespace std;
class Animal{
public:
int age;
};
class Tiger:virtual public Animal{};
class Rabbit:virtual public Animal{};
class Tibbit:public Tiger, public Rabbit{};
void test01(){
Tibbit tibbit;
//菱形继承时,两个父类拥有相同的数据,需要加上作用域进行区分
tibbit.Tiger::age = 18;
tibbit.Rabbit::age = 28;
cout << "tibbit.Tiger::age = " << tibbit.Tiger::age << endl;
cout << "tibbit.Rabbit::age = " << tibbit.Rabbit::age << endl;
//这份数据我们知道,只要有一份即可(菱形继承导致数据冗余)
}
int main(){
test01();
system("pause");
return 0;
}
注意:vbptr(virtual base pointer)虚拟基类指针,指向vbtable(virtual base table)虚拟基类表格