面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行效率的效果。
当创建一个类时,您不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。
继承代表了 is a 关系。例如,哺乳动物是动物,狗是哺乳动物,因此,狗是动物,等等。——菜鸟
1.继承语法
继承的好处:可以减少重复的代码
class A : public B;
A 类称为子类 或 派生类
B 类称为父类 或 基类
派生类中的成员,包含两大部分:
一类是从基类继承过来的,一类是自己增加的成员。
从基类继承过过来的表现其共性,而新增的成员体现了其个性。
2.继承方式
继承的语法:class 子类 : 继承方式 父类
继承方式一共有三种:
- 公共继承
- 保护继承
- 私有继承
当一个类派生自基类,该基类可以被继承为 public、protected 或 private 几种类型。继承类型是通过上面讲解的访问修饰符 access-specifier 来指定的。
我们几乎不使用 protected 或 private 继承,通常使用 public 继承。当使用不同类型的继承时,遵循以下几个规则:
- 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
- 保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
- 私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。
3.继承中的对象模型
*问题:**从父类继承过来的成员,哪些属于子类对象中?
父类的所有的非静态成员会被子类继承下去,私有成员会被编译器隐藏。
#include <iostream>
#include <string>
using namespace std;
class person
{
public:
int age;
static int a;
protected:
int moneny;
private:
int sex;
};
class teacher:public person
{
public:
int num;
};
int person::a=0;
int main()
{
class teacher t1;
cout<<"sizeof teacher= "<<sizeof(t1)<<endl;
system("pause");
return 0;
}
4.继承中构造和析构顺序
#include <iostream>
#include <string>
using namespace std;
class person
{
public:
int age;
static int a;
~person()
{
cout << "person 析构" << endl;
};
person()
{
cout << "person 构造" << endl;
};
protected:
int moneny;
private:
int sex;
};
class teacher : public person
{
public:
int num;
teacher()
{
cout << "teacher 构造" << endl;
};
~teacher()
{
cout << "teacher 析构" << endl;
};
};
void test()
{
teacher t1;
cout << "sizeof teacher= " << sizeof(teacher) << endl;
};
int person::a = 0;
int main()
{
test();
system("pause");
return 0;
}
继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反
5. 继承同名成员处理方式
#include <iostream>
#include <string>
using namespace std;
class person
{
public:
int age;
static int a;
~person()
{
cout << "person 析构" << endl;
};
person()
{
cout << "person 构造" << endl;
age=100;
};
protected:
int moneny;
private:
int sex;
};
class teacher : public person
{
public:
int age;
teacher()
{
cout << "teacher 构造" << endl;
age=200;
};
~teacher()
{
cout << "teacher 析构" << endl;
};
};
void test()
{
teacher t1;
cout << "teacher age= " << t1.age << endl;
cout << "person age= " << t1.person::age << endl;//访问子类同名成员 直接访问即可 ,访问父类同名成员 需要加作用域
};
int person::a = 0;
int main()
{
test();
system("pause");
return 0;
}
t1.person::age://访问子类同名成员 直接访问即可 ,访问父类同名成员 需要加作用域
当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数 如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域
一个派生类继承了所有的基类方法,但下列情况除外:
- 基类的构造函数、析构函数和拷贝构造函数。
- 基类的重载运算符。
- 基类的友元函数。
6.多继承语法
语法:class 子类 :继承方式 父类1 , 继承方式 父类2...
多继承可能会引发父类中有同名成员出现,需要加作用域区分
C++实际开发中不建议用多继承
class Base1 {
public:
Base1()
{
m_A = 100;
}
public:
int m_A;
};
class Base2 {
public:
Base2()
{
m_A = 200; //开始是m_B 不会出问题,但是改为mA就会出现不明确
}
public:
int m_A;
};
//语法:class 子类:继承方式 父类1 ,继承方式 父类2
class Son : public Base2, public Base1
{
public:
Son()
{
m_C = 300;
m_D = 400;
}
public:
int m_C;
int m_D;
};
//多继承容易产生成员同名的情况
//通过使用类名作用域可以区分调用哪一个基类的成员
void test01()
{
Son s;
cout << "sizeof Son = " << sizeof(s) << endl;
cout << s.Base1::m_A << endl;
cout << s.Base2::m_A << endl;
}
int main() {
test01();
system("pause");
return 0;
}
7.菱形继承
菱形继承概念:
两个派生类继承同一个基类
又有某个类同时继承者两个派生类
这种继承被称为菱形继承,或者钻石继承
菱形继承问题:
继承导致数据重复,资源浪费。
解决:使用虚继承 virtual
class Animal
{
public:
int m_Age;
};
//继承前加virtual关键字后,变为虚继承
//此时公共的父类Animal称为虚基类
class Sheep : virtual public Animal {};
class Tuo : virtual public Animal {};
class SheepTuo : public Sheep, public Tuo {};
void test01()
{
SheepTuo st;
st.Sheep::m_Age = 100;
st.Tuo::m_Age = 200;
cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;
cout << "st.Tuo::m_Age = " << st.Tuo::m_Age << endl;
cout << "st.m_Age = " << st.m_Age << endl;
}
int main() {
test01();
system("pause");
return 0;
}
虚继承--(在创建对象的时候会创建一个虚表)在创建父类对象的时候