C继承
【C++】继承
继承
继承定义
C++面向对象有三大特性
: 封装
、 继承
、 多态
。
继承机制
可以使代码 重复使用
,允许在 保持原有类特性
的基础上进行
拓展
,增加 新的成员函数
或者 成员变量
。
继承的格式
1. 下面我们看到
Person
是
基类
,也称作 父类
。 Student
是 派生类
,也称作 子类
。
- 继承类的格式如下: class 派生类:继承方式 基类
。
#include <iostream>
using namespace std;
class Students
{
public:
void identity()
{
//
}
void studying()
{
//
}
protected:
string _name;
string _address;
string _tele;
int _age;
int _id;
};
class Teachers
{
public:
void identity()
{
//
}
void teaching()
{
//
}
private:
string _name;
string _address;
string _tele;
int _age;
int _id;
};
//下⾯我们公共的成员都放到Person类中,Student和teacher都继承Person,就可以复⽤这些成员,就
不需要重复定义了,省去了很多⿇烦。
class Person
{
public:
void identity()
{
//
}
private:
string _name;
string _address;
string _tele;
int _age;
int _id;
};
class Students:public Person
{
public:
void studying()
{
//
}
};
class Teachers:public Person
{
public:
void teaching()
{
//
}
};
继承的方式
- 由于 基类
存在 3种成员变量
,而 派生类
也存在 3种继承方式
,所以存在 9种不同情况
。
基类的private成员变量
,无论以哪种方式继承, 派生类中都不可见
。不可见指的是:逻辑上成员变量 发生了继承
,但是语法上成员变量在类内、类外都 不能直接使用
, 但是可以通过调用基类的public函数来间接使用
。
- 如果 基类成员
不想在类外直接使用,又想在派生类中被继承使用,则使用 protected限定保护
,即 protected限定保护
是因为 继承
才出现的。
大小规则
: public>protected>private
,除了 基类的private成员变量
,其他成员变量的 规则
: 取两者中更小的那个
。
使用 关键字class
时默认的继承方式是 private
,使用 关键字struct
时默认的继承方式是 public
,不过最好 显示的
写出继承方式
。
6. 在实际运用中 一般使用
都是 public继承
,几乎 很少使用protetced/private继承
,因为 protetced/private继承
下来的成员都只能在派生类的 类里面使用
,实际中扩展维护性不强。
基类和派生类的转换
public继承的派生类对象
可以赋值给 基类的指针
和 基类的引用
,又称之为 切片
或者 切
割
,寓意
把派生类中基类那部分切出来
, 基类指针
或 基类引用
指向的是 派生类中切出来的基类那部分
。
基类对象不能赋值给派生类对象
。
class Person
{
protected :
string _name; // 姓名
string _sex; // 性别
int _age; // 年龄
};
class Student : public Person
{
public :
int _id; // 学号
};
int main()
{
Student sobj;
// 1.派⽣类对象可以赋值给基类的指针或引⽤
Person* pp = &sobj;
Person& rp = sobj;
// 派⽣类对象可以赋值给基类的对象是通过调⽤后⾯会讲解的基类的拷⻉构造完成的
Person pobj = sobj;
//2.基类对象不能赋值给派⽣类对象,这⾥会编译报错
sobj = pobj;
return 0;
}
继承中的作用域
隐藏规则
在 继承
体系中, 基类
和 派生类
都有 独立的作用域
。
派生类
和 基类
中有 同名成员
, 派生类成员
将 屏蔽基类对同名成员的直接访问
,这种情况叫 隐藏
。
- 在 派生类成员函数
中,可以使用 基类::基类成员 显示的访问基类的同名成员
。
4. 如果是 成员函数的隐藏
,只需要 函数名相同
就构成隐藏。
- 所以继承体系里面最好 不要定义同名的成员
。
#include <iostream>
#include <string>
using namespace std;
class Person
{
protected:
int _num=1;
};
class Student:public Person
{
public:
void Print()
{
cout<<_num<<endl;
}
protected:
int _num=11;
};
int main()
{
Student st1;
st1.Print();
return 0;
}
//11
派生类的默认成员函数
派生类
的构造函数必须调用 基类
的构造函数去 初始化基类
的那一部分成员,需要在 派生类
构造函数的初始化列表 显式调用
, 即把 基类
当成 一个整体
。此时 既需要
写基类的构造函数, 又需要
写派生类的构造函数, 还需要
在派生类构造函数的初始化列表里面显示的调用。
派生类
的拷贝构造函数
必须调用 基类
的拷贝构造
完成 基类
的那一部分成员的 拷贝
,
需要在 派生类
拷贝构造函数的初始化列表 显式调用
, 即把 基类
当成 一个整体
。如果 没有
资源的开辟消耗,那么可以 直接使用
默认拷贝构造函数,但是如果 存在
资源的开辟消耗,那么就需要 自己写
拷贝构造函数, 像构造函数一样
。
派生类
的赋值重载函数的各种情况和 派生类
的拷贝构造函数 相同
,而且 派生类
的赋值重载函数 隐藏了基类
的赋值重载函数,
所以 显示调用基类的赋值运算符
时
,需要 指定基类作用域
。
派生类
的析构函数的各种情况和 派生类
的析构函数 相同
。如果 存在
资源的开辟消耗,那么就需要 自己写
析构函数,但是不需要显示调用基类的析构函数了, 系统会自动调用
。
派生类
对象初始化 先调用基类构造再调派生类构造
。 派生类
对象析构清理 先调用派生类析构再调基类的析构
。
//构造函数
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
Person(const char* name)
:_name(name)
{}
protected:
string _name;
};
class Student:public Person
{
public:
Student(int id,const char* address,const char* name)
:_id(id)
,_address(address)
,Person(name)
{}
protected:
int _id;
string _address;
};
int main()
{
Student s1(20,"jiangxi","hsy");
return 0;
}
实现一个不能被继承的类
- 方法一: 基类的构造函数私有化
,派生类的构成必须调用基类的构造函数,但是基类的构成函数私有化以后,派生类看不见就不能调用了,那么派生类就无法实例化出对象。
- 方法二:
C++11
新增了一个 final关键字
,
派生类就不能继承了。
class Person final
{
public:
Person(const char* name)
:_name(name)
{}
protected:
string _name;
};
继承与友元
1. 友元关系 不能继承
,也就是说基类友元 不能访问
派生类私有和保护成员 。
继承与静态成员
- 基类定义了
static静态成员
,则整个继承体系里面只有一个这样的成员。无论派生出多少个派生类,都只有一个
static
成员实例。
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
string _name;
static int _count;
};
int Person::_count = 0;
class Student : public Person
{
protected:
int _stuNum;
};
int main()
{
Person p;
Student s;
// 这⾥的运⾏结果可以看到⾮静态成员_name的地址是不⼀样的
// 说明派⽣类继承下来了,⽗派⽣类对象各有⼀份
cout << &p._name << endl;
cout << &s._name << endl;
// 这⾥的运⾏结果可以看到静态成员_count的地址是⼀样的
// 说明派⽣类和基类共⽤同⼀份静态成员
cout << &p._count << endl;
cout << &s._count << endl;
// 公有的情况下,⽗派⽣类指定类域都可以访问静态成员
cout << Person::_count << endl;
cout << Student::_count << endl;
return 0;
}
多继承
单继承:一个派生类 只有一个直接基类
时称这个 继承关系
为单继承。
2. 多继承:一个派生类有 两个或以上直接基类
时称这个继承关系为多继承。
- 会出现
菱形继承
,有数据冗余和二义性的问题。