继承的好处:可以减少大量的重复代码(eg:某几个页面中重复的内容就可以用继承减少每页的代码量)
继承的语法:class A:继承方式(eg:public) B{};---A称为子类(也称为派生类)、B称为父类(也称为基类)
派生类的成员中,包含两大部分:一类是从基类中继承过来的(表现共性),一类是自己增加的成员(表现个性)
#include
using namespace std;
//创建一个基类
class base {
public:
void header()
{
cout << "页面头部包含首页、会员中心、视频、题库等(公共顶部)" << endl;
}
void rear()
{
cout << "页面尾部包含帮助中心、交流合作、站内地图等(公共底部)" << endl;
}
void left()
{
cout << "页面的左边有java、python、C++等(公共分类列表)" << endl;
}
};
//创建子类
//若不使用继承,则每个子类中都需要额外再添加基类中的三个函数代码,大大增加了重复的代码量
class java :public base {
public:
void content()//内容函数
{
cout << "此为java页面独有的内容" << endl;
}
};
class python :public base {
public:
void content()
{
cout << "此为python页面独有的内容" << endl;
}
};
class cpp :public base {
public:
void content()//内容
{
cout << "此为C++页面独有的内容" << endl;
}
};
void test01()
{
//打印输出页面中的内容
java ja;
ja.header();
ja.rear();
ja.left();
ja.content();
cout << "-------------------------------" << endl;
python py;
py.header();
py.rear();
py.left();
py.content();
cout << "-------------------------------" << endl;
cpp c;
c.header();
c.rear();
c.left();
c.content();
}
int main()
{
test01();
system("pause");
return 0;
}
继承方式有三种(分别对应三种权限):公有继承(public)、保护继承(protected)、隐私继承(private)
父类的隐私属性,子类仍然不可访问(子类内类外都不可访问)。父类的保护属性,子类中继承的仍然是保护属性(子类内可访问,类外不可访问)。父类中的公共属性,子类中仍然是公有属性(类内类外都可访问)
父类的隐私属性,子类仍然不可访问(类内类外都不可访问)。父类的保护属性和公有属性到子类都变成了保护属性(子类内可访问,类外不可访问)
父类的一切属性到类内都变成了私有属性。父类中私有属性,子类仍不能访问。父类中的其他属性,子类中类内可以访问,类外不能访问
即研究子类中是否全部继承或部分继承父类中的属性
#include
using namespace std;
//创建父类
class base1 {
public:
int m_a;
protected:
int m_b;
private:
int m_c;
};
//创建子类
class son:public base1 {
public:
int m_d;
};
void test01()
{
//对象实例化
son s;
cout << sizeof(s) << endl;
//打印结果为16,说明父类中所有非静态成员属性都会被子类继承下去(不管公有还是私有)
//注:父类中私有成员属性访问不到,是因为被编译器隐藏了,但是父类的私有成员属性仍然会被子类继承
}
int main()
{
test01();
system("pause");
return 0;
}
即先执行父类的构造函数,还是先执行子类构造函数的问题
结论:先执行父类的构造函数,再执行子类的构造函数。析构函数执行顺序与构造函数相反(构造函数先执行完,再执行析构函数)
#include
using namespace std;
//验证:
class father {
public:
father()
{
cout << "父类的构造函数" << endl;
}
~father()
{
cout << "父类的析构函数" << endl;
}
};
class son: public father {
public:
son()
{
cout << "子类的构造函数" << endl;
}
~son()
{
cout << "子类的析构函数" << endl;
}
};
void test01()
{
son s;
}
int main()
{
test01();
system("pause");
return 0;
}
结论:访问子类同名成员(属性/函数),直接访问即可。访问父类同名成员,需要加作用域(父类(eg:base)::)
#include
using namespace std;
//父类
class base {
public:
int m_a=100;
void func()
{
cout << "此为父类的func()成员函数" << endl;
}
void func(int)//int为占位符,占用一字节,作用为区分重载后的func函数
{
cout << "此为base类的func(int)成员函数" << endl;
}
};
//子类
class son:public base {
public:
int m_a = 200;//与父类中同名成员属性
void func()//与父类中同名的成员函数
{
cout << "此为子类的func()成员函数" << endl;
}
};
void test01()
{
son s;//子类对象实例化
cout << s.m_a << endl;//输出结果为200,即子类中的m_a的值
cout << s.base::m_a << endl;//输出结果为100,即父类中的m_a的值
s.func();//调用的是子类的func函数
s.base::func();//加了作用域后,调用父类的func函数。
//s.func(10);函数报错。原因是因为子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数。
//如果想访问到父类中被隐藏的同名成员函数,需要加作用域。
s.base::func(100);//调用父类的func(int)函数
}
int main()
{
test01();
system("pause");
return 0;
}
处理方法总结:与同名非静态成员处理基本一致。访问子类同名成员(属性/函数),直接访问即可。访问父类同名成员,需要加作用域(父类(eg:base)::)
静态成员特征:静态成员在内存中共享一份数据、在编译阶段分配内存、静态成员属性类内声明,类外初始化
区别:静态成员有两种调用方式
示例:
#include
using namespace std;
//创建父类
class base {
public:
static int m_a;
static void func()
{
cout << "此为父类func()静态函数" << endl;
}
static void func(int)//int为占位符
{
cout << "此为父类func(int)静态函数" << endl;
}
};
int base:: m_a=100;
//创建子类
class son:public base {
public:
static int m_a;
static void func()
{
cout << "此为子类静态函数" << endl;
}
};
int son::m_a = 200;//类外初始化
void test01()
{
//通过对象访问
cout << "通过对象访问对象属性" << endl;
son s;
cout << s.m_a << endl;//输出结果为200(子类)
cout << s.base::m_a << endl;//输出结果为100
//通过类名访问
cout << "通过对象访问对象属性" << endl;
cout << son::m_a << endl;
//第一个::表示通过类名方式访问,第二个::代表在父类的作用域下访问
cout << son::base::m_a << endl;//(输出父类的结果)
}
void test02()
{
//通过对象访问成员函数
cout << "通过对象访问对象函数:" << endl;
son s;
s.func();//访问子类的func函数
s.base::func();//访问父类的func函数
//通过类名访问成员函数
cout << "通过对象访问对象函数:" << endl;
son::func();
//第一个::表示通过类名方式访问,第二个::代表在父类的作用域下访问
son::base::func();
//son::func(100);编译器报错,因为子类出现和父类同名的静态成员函数,同样会隐藏父类中所有同名成员函数
//如果想访问父类中被隐藏的同名成员,需要加作用域
}
int main()
{
//test01();
test02();
system("pause");
return 0;
}
C++中允许一个类继承多个类(即一个子类可以继承多个父类)
语法:class 子类:继承方式 父类1,继承方式 父类2。。。。
多继承中如果父类中出现同名的情况,子类使用时要加作用域,因此C++实际开发中不建议用多继承
两个派生类(子类)继承同一个基类(父类),又有某个类(孙子类)同时继承这两个派生类(子类)。这种继承方式称为菱形继承或钻石继承
图示:
菱形继承中子类会继承两份相同的的数据,导致资源的浪费。
由于多继承的原因,子类会继承父类的数据,而若多个父类中有相同的数据A,则会造成子类成员属性访问的二义性,且毫无意义(即原本子类中只需要一份数据A即可)
利用虚继承可以解决菱形继承问题
即:
#include
using namespace std;
class father {
public:
int age;
};
//不采用虚继承的方式继承两个父类
//class base1:public father{};
//class base2 :public father{};
//采用虚继承的方式继承两个父类
//在继承方式前加virtual关键字后,变成虚继承
class base1 :virtual public father {};
class base2 :virtual public father {};
class son :public base1, public base2 {};//用公共继承方式继承base1和base2
//采用虚继承的方式继承两个父类
void test01()
{
son s;
s.base1::age = 100;
s.base2::age = 200;
//未采用虚继承方式前
//s.age;//编译器报错,因为编译器不清楚要访问的是base1的age还是base2的age
cout << s.base1::age << endl;//打印base1中的age
cout << s.base2::age << endl;//打印base2中的age
//若子类中只需要一份age数据,则可以通过虚继承来实现
//采用虚继承方式后
//打印结果都为200
s.age;
cout << s.age << endl;
cout << s.base1::age << endl;
cout << s.base2::age << endl;
}
int main()
{
test01();
system("pause");
return 0;
}