概述
C++提供了比修改代码更好的方法来扩展和修改类,这种方法叫做“类继承”,它可以从已有的类——派生出新的类。派生类继承了原有类(称为基类)的特征,包括方法。
一、基类
从一个类派生出另一个类的时候,原始类称为基类,继承类称为派生类。
下面看一个基本代码
#include <iostream>
using namespace std;
class Base
{
public:
int mA = 1;
protected:
int mB = 2;
private:
int mC = 3;
};
Base即为一个基类,在Base类当中存在三个变量处在三种访问修饰符之中:
public(公有的)、protected(受保护的)、private(私有的),这三个访问修饰符,用于修饰类的成员(属性和方法)的可见性:
public 的类成员,可以在任何地方被访问(任何地方可见)。
protected 的类成员,可以在其自身、子类和父类的内部访问。
private 的类成员,只能在其定义的类内部访问。
二、继承
图书管理系统当中,我们设计一个用户角色Base基类,当我们需要重复使用Base类当中的内容,但需要使用新添加的内容时,往往会选择重新写代码。与其从零开始,不如从Base类派生出一个新类。
2.1初识继承
通过继承派生出的类通常比设计新类容易的多。
1.可以在已有类的基础上添加功能,如:对数组类可以添加数学运算。
2.可以给类添加数据。如:对字符串,可以派生出一个类,并添加指定字符串显示颜色的数据成员。
3.可以修改类方法的行为:如:对与代表提供给乘客服务的Passenger类,可以派生出提供更高级别服务的FirstClassPassenger类。
#include <iostream>
using namespace std;
class Base
{
public:
int mA = 1;
protected:
int mB = 2;
private:
int mC = 3;
};
/*
Base 类是Son的一个public修饰父类
Son类是Base类的一个子类,或者可以认为Son类是Base类 派生类
*/
class Son :public Base
{
public :
int nA =1;
protected:
int nB =2;
private:
int nC = 3;
};
int main(int argc, char const *argv[])
{
Son *s = new Son;
cout<<" mA : "<<s->mA <<endl;
cout<<" nA : "<<s->nA <<endl;
return 0;
}
运行结果:
2.2继承权限
在代码当中我们可以看到,Son类继承父类的时候有出现了访问修饰符对所继承的父类进行修饰。
class Son :public Base
在这种情况下,我们在主函数当中访问父类中的内容、子类中的内容时,会发现报错:
int main(int argc, char const *argv[])
{
Son *s = new Son;
cout<<" mA : "<<s->mA <<endl;
cout<<" mB :"<<s->mB <<endl;
cout<<" mC :"<<s->mC <<endl;
cout<<"nA : "<<s->nA <<endl;
cout <<"nB : "<<s->nB << endl;
cout <<"nC : "<<s->nC << endl;
return 0;
}
代码当中的这四行报错。
报错原因:
首先:public:类内,子类,类外,都可以使用。
protected:类内,子类,可以使用,类外无法使用。
private: 类内,可以使用,子类,类外无法使用。
根据三个访问修饰符所作用的范围之后,我们可以明白代码当中mB、mC所属范围便无法在主函数当中访问,故无法访问。
(1)当前情况下,mB 属于 Base 类中的 protected 修饰 成员变量,同时使用 public 方式继承
子类对象无法在类外调用 mB
(2)当前情况下,mC 属于 Base 类中的 private 修饰 成员变量,同时使用 public 方式继承
子类对象无法使用 mC
2.3继承权限组合
nB、nC在class Son当中,Son所继承的Base其访问修饰符为public,思考若此处的public为其他情况时,访问子类当中的变量会出现什么样的情况。
/*
public 继承方式,父类中的成员权限修饰符变化
public-》public
protected-》protected
private 无法继承
*/
class Son1 :public Base
{
public:
void test()
{
cout <<mA <<endl;
cout <<mB <<endl;
cout <<mC <<endl;
test1();
test2();
test3();
}
};
此处代码报错:
原因:mC以及test3()在父类当中被private修饰。
/*
protected 继承方式,父类当中的成员权限修饰符编号
*/
class Son2 : protected Base
{
public:
void test()
{
cout<< mA <<endl;
cout<< mB <<endl;
cout<< mC <<endl;
test1();
test2();
test3();
}
};
此处代码报错:
原因:mC以及test3()在父类当中被private修饰。
class Son3 : private Base
{
public:
void test()
{
cout<< mA <<endl;
cout<< mB <<endl;
cout<< mC <<endl;
test1();
test2();
test3();
}
}
};
同样的,此处的mC与test3都报错:
#include <iostream>
using namespace std;
class Base
{
public:
int mA = 1;
void test1() { cout << "test1 function" << endl; }
protected:
int mB = 2;
void test2() { cout << "test2 function" << endl; }
private:
int mC = 3;
void test3() { cout << "test3 function" << endl; }
};
class Son1 : public Base
{
public:
void test()
{
cout << mA << endl;
cout << mB << endl;
// cout << mC << endl;
test1();
test2();
// test3();
}
};
class Son2 : protected Base
{
public:
void test()
{
cout << mA << endl;
cout << mB << endl;
// cout << mC << endl;
test1();
test2();
// test3();
}
};
class Son3 : private Base
{
public:
void test()
{
cout << mA << endl;
cout << mB << endl;
// cout << mC << endl;
test1();
test2();
// test3();
}
};
int main(int argc, char const *argv[])
{
Son1 *s1 = new Son1;
cout << s1->mA << endl;
Son2 *s2 = new Son2;
// cout << s2->mA << endl;
Son3 *s3 = new Son3;
// cout << s3->mA << endl;
return 0;
}
总结:
继承对应权限修饰符\原修饰符 | public | protected | private |
---|---|---|---|
public | pubilc | protected | 无法继承 |
protected | protected | protected | 无法继承 |
private | private | private | 无法继承 |
三、继承中的其他函数
3.1继承中的构造函数及其执行顺序
以下代码创建了一个父类、一个子类、以及一个子类的子类,子类继承父类的修饰符全部为public
#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;
}
};
class GrandSon :public Son
{
public:
GrandSon()
{
cout<<" GrandSon 类构造函数 "<< endl;
}
~GrandSon()
{
cout<<" GrandSon 类析构函数 "<< endl;
}
};
通过下面的主函数来调用上面的三个类
int main()
{
Son *s1 = new Son();
cout << "------------------------------------------" << endl;
delete s1;
cout << "------------------------------------------" << endl;
GrandSon *gs = new GrandSon();
cout << "------------------------------------------" << endl;
delete gs;
return 0;
}
以下为执行结果:
分析:执行下面这一行代码的时候,我们发现结果当中不仅有”son类构造函数“这一行输出还有”Base类构造函数“
Son *s1 = new Son();
同样的,在下面这一行代码执行的时候,不仅出现了本身的构造函数结果,还出现了son类和Base类的构造函数
GrandSon *gs = new GrandSon();
原因:Base类可以提供给Son类使用的内容,但需要通过Base类的构造函数进行初始化
,其内存逻辑空间展示如下图
图:Son类占用的内存空间
总结:构造函数没有创建对象的能力,创建对象都是通过new+构造函数形式进行创建的。构造函数的用于仅仅是提供数据类型和初始化申请内存空间。
Base构造函数早于Son构造函数执行,是为了初始化Base类空间提供给Son类数据的内存空间。【有其父才有其子】
3.2继承中变量名称冲突
class Base
{
public:
int num;
};
class Son : public Base
{
public:
int num;
void test()
{
//int Son :: num
cout << num << endl;
//int Base::num
cout << Base::num << endl;
}
};
当子类当中的函数调用存在与子类与父类当中相同名称变量的时候,编译器会考虑就近原则方式进行数据匹配,首选是Son类当中的num数据。
当需要使用Base中的num时,需要利用::作用域运算符限制当前变量的作用范围。
3.3继承中的函数(重写)
来继续看一段代码:
#include <iostream>
using namespace std;
class Father
{
public:
void game() { cout << "黄金矿工" << endl; }
void job() { cout << "机械工程师" << endl; }
};
class Son : public Father
{
public:
void game() { cout << "PUBG LOL 古剑奇谭 COD(Call Of Duty)" << endl; }
void job() { cout << "讲课的!" << endl; }
};
int main(int argc, char const *argv[])
{
Son *s = new Son;
s->job();
s->game();
return 0;
}
这段代码当中子类Son通过public的方式继承了父类Father,二者类内的函数其修饰符相同,都为public、并且函数名、参数列表相同,唯一不同的是输出内容不同。
代码运行之后得到以下结果:
由结果可知:
(1)子类可以通过public继承得到父类中public修饰函数,类外调用父类的函数有可能无法满足子类的特征需求,此时就需要进行父类函数的重写(Override)。
(2)函数的重写Override
存在必要的继承关系
要求子类重写父类的函数,函数声明必须一致
可以按照子类要求完成函数体内容实现。