三、继承
3.1 继承的概念
基于一个已有的类 去重新定义一个新的类,这种方式我们叫做继承
关于继承的称呼
一个类B 继承来自 类 A 我们一般称呼
A类:父类 基类
B类: 子类 派生类
B继承自A A 派生了B
示例图的语法
cpp
class vehicle // 车类
{
}
class car:public vehicle
{
}
3.2 继承的意义
1、可以实现代码复用,减少重复代码的劳动量
2、继承是实现多态的必要条件
3、继承本身就是为了实现多态的,顺便实现了代码复用
3.3 继承的基本语法
3.3.1 使用方法
3.3.1.1 基本语法
cpp
class 子类名称:继承方式 父类名称
{
子类新增的成员;
}
class B:public A
{
}
3.3.1.2 基类对象初始化
在子类的构造函数中,通常需要在初始化列表中显式调用基类的构造函数,以完成对基类成员的初始化。例如:
在继承时,需要在子类的构造函数的初始化表中
显性的调用父类的构造函数 来完成对父类成员的初始化格式
构造函数名(int val_1 , int val_2): class_A(val_1 , val_2);
3.3.1.3 派生类中调用基类
在派生类中可以通过以下方式调用基类的成员:
类外访问:
对象名.基类名::变量;
对象名.基类名::函数名(函数的实参表);
类内访问:
基类名::变量;
基类名::函数名(函数的实参表);
3.3.2 示例代码一(基本语法1)
cpp
#include <iostream>
#include <string>
using namespace std;
class person
{
private:
public:
string name;
int id;
int height;
public:
person(string name , int id , int height)
:name(name) , id(id) , height(height)
{
cout << "父亲构造函数" << endl;
}
};
class man : public person
{
private:
string name;
public:
man(string name , int id ,int height)
: person(name , id ,height)
{
}
void show_data()
{
cout << person::name << " " << person::id << " " << this->height << endl ;
}
};
int main(int argc, char const *argv[])
{
man m("张三" , 1 , 131);
m.show_data();
return 0;
}
示例代码1 运行结果如下:
3.3.3 示例代码二(基本语法2)
cpp
#include <iostream>
#include <string>
using namespace std;
class person
{
private:
public:
string name;
string sex;
int age;
public :
person(string name ,int age ,string sex)
:name(name), age(age) ,sex(sex)
{
cout << "父类有参构造" << endl;
}
person()
{
name = "未设置姓名";
age = -1;
sex = "沃尔玛塑料袋";
cout << "父类无参构造" << endl;
}
~person()
{
cout << "我是父类的析构" << endl ;
}
};
class student:public person
{
private:
public:
int id;
public:
student(string name , string sex ,int age ,int id)
:person(name , age , sex ) , id(id)
{
cout << "子类有参构造" << endl;
}
student()
{
name = "未设置姓名";
age = -1;
sex = "沃尔玛塑料袋";
id = -1 ;
cout << "子类无参构造" << endl;
}
~student()
{
cout <<"我是子类的析构" << endl ;
}
public:
void show_data()
{
cout << "姓名 " << name << endl;
cout << "年龄 " << person::age << endl;
cout << "性别 " << this->sex << endl;
cout << "学号 " << this->id << endl;
}
};
int main(int argc, char const *argv[])
{
student s1 ;
s1.name ="张三";
s1.person::age = 10;
s1.sex = "男";
s1.id = 10;
s1.show_data();
return 0;
}
示例代码2 运行结果如下:
3.4 继承的方式
3.4.1 类中的访问控制权限
类内 | 子类 | 类外 | |
---|---|---|---|
public | √ | √ | √ |
protected | √ | √ | × |
private | √ | × | × |
而在继承中 也有三种访问方式:public 、protected 、private
- 公共继承 public
- 保护继承 protected
- 私有继承 private
访问关系如下图:
3.4.2 示例代码三(继承方式
cpp
#include <iostream>
#include <string>
using namespace std;
class person
{
private:
string name;
private:
int age;
protected:
string sex;
public:
person(string name ,int age ,string sex)
:name(name) , age(age) , sex(sex)
{
cout << "父类有参构造" << endl;
}
person()
{
name = "未设置姓名";
age = -1 ;
sex = "沃尔玛塑料袋";
cout << "父类无参构造" << endl;
}
~person()
{
cout << "我是父类的析构" << endl;
}
};
class student : private person
{
private:
public:
int id;
public:
student(string name ,string sex ,int age ,int id)
:person(name ,age ,sex),id(id)
{
cout <<"子类有参构造" << endl;
}
student()
{
cout << "子类无参构造" << endl;
}
~student()
{
cout<< "我是子类的析构" << endl;
}
public:
void show_data()
{
}
};
class son : private student
{
private:
public:
son()
{
student::id = 10;
}
~son()
{
}
};
int main(int argc, char const *argv[])
{
son s;
while (1)
{
/* code */
}
return 0;
}
示例代码3 运行结果如下:
3.5 继承的底层解释
实际开发中 一般继承都采用 **pubilc
**继承方式
如果不写继承方式,默认使用的都是**private
** 方式继承
1、子类中会继承父类中所有成员 ,包括私有成员,只不过在子类中不能直接访问父类私有成员需要父类提供公有的函数来访问父类的私有成员
2、当父子类中出现了同名的函数时,访问来也不会冲突,即使形参不同,也不构成重载关系 愿意是两个函数不在同一个空间内,如果想访问父类的成员 需要加 类名 :: 来修饰
类外访问:
对象名.基类名::变量;
对象名.基类名::函数名(函数的实参表);
类内访问:
基类名::变量;
对象名.基类名::函数名(函数的实参表);
3.5.1 示例代码四(继承模型)
cpp
#include <iostream>
#include <string>
using namespace std;
class person
{
private:
string name; // 姓名
string sex; // 性别
int age; // 年龄
public:
void set_name(string name)
{
this->name = name;
cout << name << endl;
}
};
class student:public person
{
private:
public:
//1、子类如何访问父类的私有成员
void set_data(string name , string sex , int age )
{
person::set_name(name);
}
//2、当父类 和 子类 函数名冲突时 咋办
void set_name(string name)
{
person::set_name(name);
}
~student()
{
person();
}
};
int main(int argc, char const *argv[])
{
student s1;
s1.set_name("张三");
student s2;
s2.set_data("王五","男",11);
person p1;
p1.set_name("李四");
return 0;
}
示例代码4 运行结果如下:
##拓展 在继承中析构和构造的区别
#构造
1、父类的构造函数不会被子类继承
2、需要在子类的构造函数的初始列表中,显性的调用父类函数的构造函数
完成对父类中继承过来的成员初始化
3、如果没有在子类的构造函数初始化列表中调用父类中的构造函数 则使用无参构造
如果父类没有无参构造 会报错
4、构造函数的调用顺序是
先调用父类函数的构造函数
再调用子类函数的构造函数
#析构
1、父类的析构函数不会被子类继承
2、不管是否显性调用父类的析构函数,父类的析构函数都会被调用
完成对父类中继承过来的成员的善后工作
3、子类的析构函数中 无需调用父类的析构函数
4、析构函数的调用顺序是
先调用子类函数的析构函数
再调用父类函数的析构函数
**前情回顾:**
第一篇
第二篇