【C++】c++中的继承

1、什么是继承?
我们都知道继承性在客观世界中是一种常见的现象。在我们了解C++的继承之前,让我们先介绍一个现实世界中“动物”的例子,它们之间的属性继承关系如图所示:
这里写图片描述
从面向对象的观点来看,继承所表达的正是这样一种类与类之间的关系,这种关系允许既有类在的基础上创建新类,增加了软件的重用性,减少了工作量。
继承方式不同所引起的访问控制关系变化:
这里写图片描述
2、基类与派生类
C++允许从一个类“派生”其他的类,当然即将派生的新类与既有类之间是存在一定的内在联系的。可以按如下描述形象地理解“派生”:父辈类生下了“儿子”类,例如,由于“哺乳动物”类是“有脊椎动物”类的一个特例,所以可以从“有脊椎动物”类派生出“哺乳动物”类。在这个派生过程中,“下一级的类”继承了“上一级”的属性。
在继承关系里面,在派生类中如果没有显示定义这六个成员函数,编译系统则会默认合成这六个默认成员函数:
这里写图片描述
这里我们还应注意几点问题:
(1).public继承是一个接口继承,保持is-a原则,每个父类可用的成员对子类也可用,因为每个子类对象也都是一个父类对象。(注意:可以把基类对象赋值给派生类,但不可以把派生类对象赋值给基类)
(2). protected/private继承是一个实现继承,基类的部分成员并非完全成为子类接口的一部分,是 has-a 的关系原则,所以非特殊情况下不会使用这两种继承关系,在绝大多数的场景下使用的都是公有继承。私有继承以为这is-implemented-in-terms-of(是根据……实现的)。通常比组合(composition)更低级,但当一个派生类需要访问基类保护成员或需要重定义基类的虚函数时它就是合理的。1
(3).不管是哪种继承方式,在派生类内部都可以访问基类的公有成员和保护成员,基类的私有成员存在但是在子类中不可见(不能访问)。
3、单继承&多继承&菱形继承1
继承的一般形式为:

class 派生类名:(继承方式)基类名
{
派生类新增成员;
}

【单继承】
一个类只有一个直接父类时称这个继承关系为单继承。

class Person
{
protected:
    string _name;
};
class Student : public Person
{
protected:
     int _stuNum; //学号
};
class Granduate : public Student
{
protected:
string _seminarCourse; //研究科目
}

这里写图片描述
【多继承】
即一个派生类继承多个基类。
这里写图片描述
例如下面的例子:类Z就是一个多重继承的派生类,它有两个基类,分别是类X和类Y.

class X
{
public:
   X(int n);
   ~X();
protected:
   int _x;
};
class Y
{
public:
   Y(double d);
   ~Y();
protected:
   int _y;
};
class Z : public X,public Y 
{
public:
   Z(int n,double d);
   ~Z();
protected:
   int _z;
};

【菱形继承】
这里写图片描述

#include<iostream>
using namespace std;
class A
{
public:
int _a;
};
class X : public A
{
public:
    void fun1()
    {}
protected:
int _x;
};
class Y : public A
{
public:
    void fun1()
    {}
protected:
int _y;
};
class Z : public X,public Y
{
protected:
int _z;
};
int main()
{
    Z* obj = new Z();
    obj->fun1();       //错误
}

这里写图片描述

由于Z从X和Y继承,因此当调用函数fun1()时,编译器就不知道调用类X还是类Y的成员,因此编译器会输出如下错误:
这里写图片描述
我们可以这样解决:

void main()
{
    Z* obj = new Z();
    obj->X::fun1();      //明确指定调用X的成员函数
    obj->X::_a();    //显示指定哪个父类成员
    obj->Y::_a();    
}

这里写图片描述

我们可以通过虚继承更好的解决这个问题。
虚继承–解决菱形继承的二义性和数据冗余的问题
1. 虚继承解决了在菱形继承体系里面子类对象包含多份父类对象的数据冗余&浪费空间的问题。
2. 虚继承体系看起来好复杂,在实际应用我们通常不会定义如此复杂的继承体系。一般不到万不得已都不要定义菱形结构的虚继承体
系结构,因为使用虚继承解决数据冗余问题也带来了性能上的损耗。
这里写图片描述
这里写图片描述

4、特别注意:
(1)友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员。

class Person
{
friend void Display(Person &p , Student&s);
protected :
string _name ; // 姓名
};
class Student: public Person
{
protected :
int _stuNum ; // 学号
};
void Display(Person &p , Student &s)
{
cout<<p._name<<endl;
cout<<s._name<<endl;
cout<<s._stuNum<<endl;
}
void TestPerson1()
{
Person p;
Student s;
Display (p, s);
}

将会出现如下报错:
这里写图片描述
(2)基类定义了static成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例。
剖析虚继承是怎么解决二义性和数据冗余的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章