首页
社区
课程
招聘
[分享]C++基础十二-继承
发表于: 2021-9-26 18:12 7361

[分享]C++基础十二-继承

2021-9-26 18:12
7361

继承机制是面向对象程序设计使代码可以复用的重要手段,它允许一个类在保持原有特性的基础上进行扩展,增加功能,这样产生新的类,称作派生类。继承呈现了面向对象程序设计的层析结构,体现了由简单到复杂的认知过程,比如从野生动物概念到详细具体的犬科动物狗。
派生(Derive)和继承(Inheritance)是一个概念,只是站的角度不同。被继承的类称为父类或基类,继承的类称为子类或派生类。“子类”和“父类”通常放在一起称呼,“基类”和“派生类”通常放在一起称呼。

继承方式限定了基类成员在派生类中的访问权限,包括public(公有的)、private(私有的)和 protected(受保护的)。

1、基类中所有 public 成员在派生类中为 public 属性。
2、基类中所有 protected 成员在派生类中为 protected 属性。
3、基类中所有 private 成员在派生类中不能使用。

1、基类中的所有 public 成员在派生类中为 protected 属性。
2、基类中的所有 protected 成员在派生类中为 protected 属性。
3、基类中的所有 private 成员在派生类中不能使用。

1、基类中的所有 public 成员在派生类中均为 private 属性。
2、基类中的所有 protected 成员在派生类中均为 private 属性。
3、基类中的所有 private 成员在派生类中不能使用。

派生类从基类中继承过来的成员(函数、变量)可能和派生类部分成员(函数、变量)重名,同名成员变量可以通过作用域区分,同名成员函数则有三种关系:重载、隐藏和覆盖。

函数重载有三个条件,一函数名相同,二形参类型、个数、顺序不同,三作用域相同。根据第三个条件,可知函数重载只可能发生在一个类中。

在派生类中将基类中的同名成员方法隐藏,要想在派生类对象中访问基类同名成员得加上基类作用域。

基类、派生类中的同名方法,函数头相同(参数、返回值),且基类中该方法为虚函数,则派生类中的同名方法将基类中方法覆盖。这里涉及到了虚函数的问题,后续进行讲解。函数隐藏和函数覆盖都是发生在基类和派生类之间的,基类和派生类中的同名函数,除去是覆盖的情况,其他都是隐藏的情况。

前面说过,派生类将基类中除去构造函数和析构函数的其他方法继承了过来,那么对于派生类对象中自己的成员变量和来自基类的成员变量,应该怎么初始化呢?大部分基类都有private 属性的成员变量,它们在派生类中无法直接访问,更不能使用派生类的构造函数直接初始化,解决这个问题的思路是,在派生类的构造函数中调用基类的构造函数。

析构函数和构造函数解决问题的方法一样,但是与构造函数不同的是,在派生类的析构函数中不用显式地调用基类的析构函数,编译器会自动选择隐式调用基类的析构函数。
另外析构函数的执行顺序和构造函数的执行顺序刚好相反:

派生类只有一个基类,称为单继承(Single Inheritance)。同时C++ 也支持多继承(Multiple Inheritance),即一个派生类可以有两个或多个基类。多继承容易让代码逻辑复杂、思路混乱,一直备受争议,后来的C#、Java、PHP 等取消了多继承。

多继承形式下的构造函数和单继承形式基本相同,只是要在派生类的构造函数中调用多个基类的构造函数。基类构造函数的调用顺序和和它们在派生类构造函数中出现的顺序无关,而是和声明派生类时基类出现的顺序相同。

当两个或多个基类中有同名的成员时,如果直接访问该成员,就会产生命名冲突,编译器不知道使用哪个基类的成员。这个时候需要在成员名字前面加上类名和域解析符::,以显式地指明到底使用哪个类的成员,消除二义性。

多继承是指从多个直接基类中产生派生类的能力,多继承的派生类继承了所有父类的成员。尽管概念上非常简单,但是多个基类的相互交织可能会带来错综复杂的设计问题,命名冲突就是不可回避的一个,比如典型的菱形继承。

在一个派生类中保留间接基类的多份同名成员,虽然可以在不同的成员变量中分别存放不同的数据,但大多数情况下这是多余的,因为保留多份成员变量不仅占用较多的存储空间,还容易产生命名冲突。

为了解决多继承时的命名冲突和数据冗余问题,C++ 中提出了虚继承的概念,使得在派生类中只保留一份间接基类的成员。C++ 中定义了virtual 关键字,用来修饰继承关系,实现虚继承。

虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类(Virtual Base Class),上面例子中CTravelTool 就是一个虚基类。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员。

因为在虚继承的最终派生类中只保留了一份虚基类的成员,所以该成员可以被直接访问,不会产生二义性。
以菱形继承为例,假设CTravelTool 类中定义了一个成员变量m_nNumber ,当我们在CFlyingCar 类中直接访问m_nNumber 时,会有三种可能性:

因此只有在比较简单和不易出现二义性的情况下或实在必要时才使用多继承,能用单一继承解决的问题就不要使用多继承。也正是这个原因,C++ 之后的很多面向对象的编程语言如C#、Java、PHP 等都不支持多继承。

#pragma once
 
class CAnimal
{
public:
    CAnimal(int nAge = 0, int dbWeight = 0);
    ~CAnimal();
    void EatAndDrink();
    void Sleep();
    void ShowInfo();
 
protected:
    int m_nAge;
    int m_dbWeight;
};
 
class CDog :public CAnimal
{
public:
    CDog(int nAge = 0, int dbWeight = 0, char* szBelong = (char*)"canine");
    ~CDog();
    void SetBelong(char* szBelong);
    char* GetBelong();
    void Bark();
    void ShowInfo();
 
private:
    char m_szBelong[128];
};
#pragma once
 
class CAnimal
{
public:
    CAnimal(int nAge = 0, int dbWeight = 0);
    ~CAnimal();
    void EatAndDrink();
    void Sleep();
    void ShowInfo();
 
protected:
    int m_nAge;
    int m_dbWeight;
};
 
class CDog :public CAnimal
{
public:
    CDog(int nAge = 0, int dbWeight = 0, char* szBelong = (char*)"canine");
    ~CDog();
    void SetBelong(char* szBelong);
    char* GetBelong();
    void Bark();
    void ShowInfo();
 
private:
    char m_szBelong[128];
};
#include "Animal.h"
#include <iostream>
using namespace std;
 
CAnimal::CAnimal(int nAge, int dbWeight) :m_nAge(nAge), m_dbWeight(dbWeight)
{
}
 
CAnimal::~CAnimal()
{
}
 
void CAnimal::EatAndDrink()
{
    cout << "animal eat and drink" << endl;
}
 
void CAnimal::Sleep()
{
    cout << "animal sleep" << endl;
}
 
void CAnimal::ShowInfo()
{
    cout << "animal:age=" << m_nAge << ",weight=" << m_dbWeight << endl;
}
 
CDog::CDog(int nAge, int dbWeight, char* szBelong) :CAnimal(nAge, dbWeight)
{
    memset(m_szBelong, 0, 128);
    strcpy_s(m_szBelong, szBelong);
}
 
CDog::~CDog()
{
    memset(m_szBelong, 0, 128);
}
 
void CDog::SetBelong(char* szBelong)
{
    memset(m_szBelong, 0, 128);
    strcpy_s(m_szBelong, szBelong);
}
 
char* CDog::GetBelong()
{
    return m_szBelong;
}
 
void CDog::Bark()
{
    cout << "dog wangwang" << endl;
}
 
void CDog::ShowInfo()
{
    cout << "dog:belong=" << m_szBelong << ",age=" << m_nAge << ",weight=" << m_dbWeight << endl;
}
#include "Animal.h"
#include <iostream>
using namespace std;
 
CAnimal::CAnimal(int nAge, int dbWeight) :m_nAge(nAge), m_dbWeight(dbWeight)
{
}
 
CAnimal::~CAnimal()
{
}
 
void CAnimal::EatAndDrink()
{
    cout << "animal eat and drink" << endl;
}
 
void CAnimal::Sleep()
{
    cout << "animal sleep" << endl;
}
 
void CAnimal::ShowInfo()
{
    cout << "animal:age=" << m_nAge << ",weight=" << m_dbWeight << endl;
}
 
CDog::CDog(int nAge, int dbWeight, char* szBelong) :CAnimal(nAge, dbWeight)
{
    memset(m_szBelong, 0, 128);
    strcpy_s(m_szBelong, szBelong);
}
 
CDog::~CDog()
{
    memset(m_szBelong, 0, 128);
}
 
void CDog::SetBelong(char* szBelong)
{
    memset(m_szBelong, 0, 128);
    strcpy_s(m_szBelong, szBelong);
}
 
char* CDog::GetBelong()
{
    return m_szBelong;
}
 
void CDog::Bark()
{
    cout << "dog wangwang" << endl;
}
 
void CDog::ShowInfo()
{
    cout << "dog:belong=" << m_szBelong << ",age=" << m_nAge << ",weight=" << m_dbWeight << endl;
}
#include <iostream>
using namespace std;
#include "Animal.h"
 
int main()
{
    CAnimal animal;
    animal.ShowInfo();
    animal.EatAndDrink();
    animal.Sleep();
 
    CDog DogA;
    DogA.ShowInfo();
    DogA.CAnimal::ShowInfo();
    CDog DogB(3, 6, (char*)"canine");
    DogB.ShowInfo();
    DogB.EatAndDrink();
    DogB.Sleep();
    DogB.Bark();
 
    return 0;
}
#include <iostream>
using namespace std;
#include "Animal.h"
 
int main()
{
    CAnimal animal;
    animal.ShowInfo();
    animal.EatAndDrink();
    animal.Sleep();
 
    CDog DogA;
    DogA.ShowInfo();
    DogA.CAnimal::ShowInfo();
    CDog DogB(3, 6, (char*)"canine");
    DogB.ShowInfo();
    DogB.EatAndDrink();
    DogB.Sleep();
    DogB.Bark();
 
    return 0;
}
#pragma once
//多重继承实现可折叠沙发床
class CSoft
{
public:
    CSoft(double dbWeight = 0);
    ~CSoft();
    void Function();
public:
    double m_dbWeigth;
};
 
class CBed
{
public:
    CBed(double dbWeight = 0);
    ~CBed();
    void Function();
public:
    double m_dbWeigth;
};
 
class CSoftbed :public CSoft, public CBed
{
public:
    CSoftbed(double dbWeight = 0);
    ~CSoftbed();
    void Function();
};
#pragma once
//多重继承实现可折叠沙发床
class CSoft
{
public:
    CSoft(double dbWeight = 0);
    ~CSoft();
    void Function();
public:
    double m_dbWeigth;
};
 
class CBed
{
public:
    CBed(double dbWeight = 0);
    ~CBed();
    void Function();
public:
    double m_dbWeigth;
};
 
class CSoftbed :public CSoft, public CBed
{
public:
    CSoftbed(double dbWeight = 0);
    ~CSoftbed();
    void Function();
};
#include "SofeBed.h"
#include <iostream>
using namespace std;
 
CSoft::CSoft(double dbWeight) :m_dbWeigth(dbWeight)
{
}
CSoft::~CSoft()
{
}
void CSoft::Function()
{
    cout << "Sit" << endl;
}
 
CBed::CBed(double dbWeight) :m_dbWeigth(dbWeight)
{
}
CBed::~CBed()
{
}
void CBed::Function()
{
    cout << "Lie" << endl;
}
 
CSoftbed::CSoftbed(double dbWeight) : CSoft(dbWeight), CBed(dbWeight)
{
}
CSoftbed::~CSoftbed()
{
}
void CSoftbed::Function()
{
    cout << "Sit and Lie" << endl;
}
#include "SofeBed.h"
#include <iostream>
using namespace std;
 
CSoft::CSoft(double dbWeight) :m_dbWeigth(dbWeight)
{
}
CSoft::~CSoft()
{
}
void CSoft::Function()
{
    cout << "Sit" << endl;
}
 
CBed::CBed(double dbWeight) :m_dbWeigth(dbWeight)
{
}
CBed::~CBed()
{
}
void CBed::Function()
{
    cout << "Lie" << endl;
}
 
CSoftbed::CSoftbed(double dbWeight) : CSoft(dbWeight), CBed(dbWeight)
{
}
CSoftbed::~CSoftbed()
{
}
void CSoftbed::Function()
{
    cout << "Sit and Lie" << endl;
}
#include "SofeBed.h"
#include <iostream>
using namespace std;
//直接定义可折叠沙发床的类
class CSoftBed
{
public:
    CSoftBed(double dbWeight = 0);
    ~CSoftBed();
    double GetWeight();
    CSoft GetSoft();
    CBed GetBed();
    void Function();
 
private:
    double m_dbWeigth;
    CSoft m_soft;
    CBed m_bed;
};
 
CSoftBed::CSoftBed(double dbWeight) :m_soft(0), m_bed(0), m_dbWeigth(dbWeight)
{
}
CSoftBed::~CSoftBed()
{
}
double CSoftBed::GetWeight()
{
    return m_dbWeigth;
}
CSoft CSoftBed::GetSoft()
{
    return m_soft;
}
CBed CSoftBed::GetBed()
{
    return m_bed;
}
void CSoftBed::Function()
{
    cout << "Sit and Lie" << endl;
}
 
int main()
{
    //使用多重继承
    CSoftbed softbed(1);
    //cout << softbed.m_dbWeigth << endl; //m_dbWeigth不明确
    cout << softbed.CSoft::m_dbWeigth << endl;
    cout << softbed.CBed::m_dbWeigth << endl;
    softbed.Function();
    softbed.CSoft::Function();
    softbed.CBed::Function();
 
    //直接使用类
    CSoftBed softBed(9);
    cout << softBed.GetWeight() << endl;
    cout << softBed.GetSoft().m_dbWeigth << endl;
    cout << softBed.GetBed().m_dbWeigth << endl;
    softBed.Function();
    softBed.GetSoft().Function();
    softBed.GetBed().Function();
 
    return 0;
}
#include "SofeBed.h"
#include <iostream>
using namespace std;
//直接定义可折叠沙发床的类
class CSoftBed
{
public:
    CSoftBed(double dbWeight = 0);
    ~CSoftBed();
    double GetWeight();
    CSoft GetSoft();
    CBed GetBed();
    void Function();
 
private:
    double m_dbWeigth;
    CSoft m_soft;
    CBed m_bed;
};
 
CSoftBed::CSoftBed(double dbWeight) :m_soft(0), m_bed(0), m_dbWeigth(dbWeight)
{
}
CSoftBed::~CSoftBed()

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 1
支持
分享
最新回复 (1)
游客
登录 | 注册 方可回帖
返回
// // 统计代码