C++基础杂记(3)

发布时间 2023-10-31 13:26:56作者: Torch_HXM


类的继承

基类与派生类之间的构造行为

派生类可以调用基类的公共成员,但无法调用基类的私有成员。所以派生类无法直接初始化基类中的私有成员变量。在派生类中初始化基类的私有成员变量需要显式的调用基类的构造函数,并以委托构造的方式将其初始化。

// 基类头文件 basic.h
#ifndef __BASIC__
#define __BASIC__

class Basic
{
private:
    int basic_pri_val;
    void basicPriFuc() const;
public:
    Basic(int _pri_val, int _pub_val):basic_pri_val(_pri_val), basic_pub_val(_pub_val){}
    int basic_pub_val;
    void basicPubFuc() const;
    void showBasicPriVal() const;
};
#endif
// 基类源文件 basic.cpp
#include <iostream>
#include "basic.h"
using std::cout;
void Basic::basicPriFuc() const
{
    cout<< "Basic private function.\n";
}

void Basic::basicPubFuc() const
{
    cout<< "Baisc public function.\n";
}

void Basic::showBasicPriVal() const
{
    cout<< "Basic private value: "<< basic_pri_val<< '\n';
}
// 派生类头文件 derive.h
#ifndef __DERIVE__
#define __DERIVE__

#include "basic.h"

class Derive:public Basic   // 从 Basic 类派生
{
public:
    Derive(int _basic_pri_val, int _basic_pub_val):Basic(_basic_pri_val, _basic_pub_val){}  // 委托基类构造函数初始化基类成员变量
};

#endif
// 派生类源文件 derive.cpp
#include "derive.h"
// 主程序源文件 main.cpp
#include "derive.h"

int main()
{
    Derive derive = {1, 2};

    // derive.basicPriFuc();    // 不可访问基类私有成员函数
    derive.basicPubFuc();       // 可以访问基类公共成员函数
    // derive.basic_pri_val;    // 不可访问基类私有成员变量
    derive.showBasicPriVal();   // 基类私有成员变量可以通过基类公共成员函数访问
    derive.basic_pub_val;       // 可以访问基类公共成员变量

    return 0;
}

Baisc public function.
Basic private value: 1

另外,基类的引用或指针可以指向派生类,但这样的引用和指针只能调用来自于基类的方法。

在派生类中使用基类方法

在派生类中使用基类方法只需要在方法前加上域解析运算符,如下:

// 假定类 Basic 中已经定义了 myPrint 方法
class Derive:: public Basic
{
public:
    void myPrint()
    {
        Basic::myPrint();   // 调用类 Basic 中的 myPrint 方法
        myPrint();          // 调用类 Derive 中的 myPrint 方法
    }
}

protected 的访问权限

对于类外而言,protected 的访问权限与 private 相似。对于派生类而言, protected 的访问权限与 public 相似。

多态公有继承

在进行本节之前需要知道:父类的指针/引用可以指向/引用派生类,但只能调用继承于父类的方法或重写在子类中的虚方法。

多态有静态多态和动态多态两个部分。

  • 静态多态相当于在派生类中重写了(不是重载)父类中的函数,函数的声明中,形参类型和数量可以与父类不一致。派生类只能调用重写后的函数,而不能够再使用被重写的父类中的函数。
  • 动态多态发生在使用父类指针或引用,调用或重写修函数时。下面将进行详细的演示。

关键字 virtual

  • 只有类的非静态成员函数可以成为虚函数,其他非类成员函数或类的静态成员函数不行。
  • 类中的虚函数,即使不被调用也必须被定义。而非虚函数在不被调用的情况下可以只声明不定义。
  • 派生类与父类中的虚函数声明必须完全一致,包括函数名与形参类型。
  • virtual 还可用于类的虚继承。

使用 virtual 修饰类的成员函数,其成员函数的调用行为只在下列情况下不同:

  • 对于一般的成员函数,如果使用类的指针或者引用来调用该函数,则函数的行为将取决于声明该指针/引用的类型。即如果是父类类型的的指针/引用,则将调用父类中的函数;如果是派生类类型的指针/引用,则将调用派生类中的函数。
  • 对于虚成员函数,如果使用类的指针或引用来调用该函数,则函数的行为取决于指针所指向的对象的类型。

示例

// 基类头文件 basic.h
#ifndef __BASIC__
#define __BASIC__

class Basic
{
private:
    int basic_pri_val;
    void basicPriFuc() const;
public:
    Basic(int _pri_val, int _pub_val):basic_pri_val(_pri_val), basic_pub_val(_pub_val){}
    // virtual ~Basic() {}; // 在包含虚函数时,应当包含一个虚析构函数
    int basic_pub_val;
    void basicPubFuc() const;
    void showBasicPriVal() const;

    void myPrint() const;   // 非虚函数,下面将在派生类中重写
};
#endif
// 基类源文件 basic.cpp
#include <iostream>
#include "basic.h"
using std::cout;
void Basic::basicPriFuc() const
{
    cout<< "Basic private function.\n";
}

void Basic::basicPubFuc() const
{
    cout<< "Baisc public function.\n";
}

void Basic::showBasicPriVal() const
{
    cout<< "Basic private value: "<< basic_pri_val<< '\n';
}

void Basic::myPrint() const
{
    cout<< "Basic class print.\n";
}
// 派生类头文件 derive.h
#ifndef __DERIVE__
#define __DERIVE__

#include "basic.h"

class Derive:public Basic   // 从 Basic 类派生
{
public:
    Derive(int _basic_pri_val, int _basic_pub_val):Basic(_basic_pri_val, _basic_pub_val){}   // 委托基类构造函数初始化基类成员变量

    void myPrint();     // 重写父类中的非虚函数

};

#endif
// 派生类源文件 derive.cpp
#include "derive.h"
using std::cout;
void Derive::myPrint() const
{
    cout<< "Derive class print.\n";
}
// 主程序源文件 main.cpp
#include "derive.h"

int main()
{
    Derive derive = {1, 2};
    Basic & basic = derive;
    basic.myPrint();

    return 0;
}

Basic class print.

当重写父类中的普通函数时,即使父类引用引用了派生类,但是该引用调用的却是父类中的函数。myPrint 函数的行为却决于引用类型。

如果我们在声明中为 myPrint() 函数加上 virtual 修饰,则程序结果为:

Derive class print

可以看到,myPrint() 函数的行为取决于引用所指向的类。

如果需要在派生类中重定义基类中的方法,则应尽量将该方法声明为虚。且需要在基类中声明一个虚析构函数。

当类被用作基类时,它的虚构函数应当被设置为虚函数,否则将会对派生类对象的内存释放造成麻烦。

抽象基类(ABC)

类中包含纯虚函数的类被称为抽象基类。纯虚函数的声明如下:

class ABC
{
public:
    virtual void myPrint() const = 0;
}

纯虚函数可以在抽象基类中被定义。抽象基类的派生类必须使用重写虚函数的方法来重写纯虚函数。抽象基类强制派生类实现其纯虚函数。

私有继承和保护继承

  • 在私有继承中,基类的公有成员和保护成员都将成为派生类的私有成员。
  • 在保护继承中,基类的公有成员和保护成员都将成为派生类的保护成员。

多重继承

class Deriver : public Basic1, private Basic2 {···};