8、C++面向程序设计:虚函数

1、面向对象多态的思想

面向对象多态是面向对象编程中的一个重要概念,指的是同一种对象或行为的不同表现形式。通过多态,可以以统一的方式处理不同类型的对象,提高代码的灵活性和可扩展性。

使用多态的主要目的是增加代码的可复用性和可扩展性。通过将共同行为抽象成接口或基类,不同的子类可以实现自己独特的行为,但在使用时可以通过统一的接口进行操作,而不需要考虑具体的子类类型。这样可以使代码更加灵活,便于后续的扩展和维护。下面先举个简单的例子来说明,相关知识点稍后将要学习,读者先对其有个印象,先不必纠结不懂的知识点,最好亲自动手上机书写代码并调试。

示例:

下面是一个简单的关于电脑的例子,通过多态实现了不同类型的设备(键盘、鼠标、显示器)的连接和操作。

// 设备基类

class Device {

public:

    virtual void connect() {

        std::cout << “Device connected.” << std::endl;

    }

    virtual void operate() = 0;  // 纯虚函数,需要在子类中实现

};

// 键盘类

class Keyboard : public Device {

public:

    void connect() override {

        std::cout << “Keyboard connected.” << std::endl;

    }

    void operate() override {

        std::cout << “Typing…” << std::endl;

    }

};

// 鼠标类

class Mouse : public Device {

public:

    void connect() override {

        std::cout << “Mouse connected.” << std::endl;

    }

    void operate() override {

        std::cout << “Moving mouse…” << std::endl;

    }

};

// 显示器类

class Monitor : public Device {

public:

    void connect() override {

        std::cout << “Monitor connected.” << std::endl;

    }

    void operate() override {

        std::cout << “Displaying…” << std::endl;

    }

};

// 主函数

int main() {

    Device* devices[3];  // 设备数组

    Keyboard keyboard;

    Mouse mouse;

    Monitor monitor;

    devices[0] = &keyboard;

    devices[1] = &mouse;

    devices[2] = &monitor;

    for (int i = 0; i < 3; i++) {

        devices[i]->connect();

        devices[i]->operate();

    }

    return 0;

}

在上述例子中,通过将设备抽象成基类Device,每个设备类型都继承自Device并实现自己的connect和operate方法。通过使用多态,在主函数中可以统一以Device指针的形式访问不同类型的设备,并调用相应的方法。这样就可以以统一的方式处理不同设备的连接和操作,提高了代码的可复用性和可扩展性。

2、虚函数

(1)为什么使用虚函数

C++中的虚函数是在基类中声明并在派生类中重写的成员函数。使用关键字`virtual`来声明一个虚函数。当使用基类指针或引用调用虚函数时,根据对象的实际类型,程序会动态地调用相应的派生类中的函数。

虚函数的使用有以下几个好处:

(1)实现运行时多态性:通过虚函数,可以在运行时选择正确的函数实现,而不是在编译时静态地绑定到基类函数。这样,可以根据对象的实际类型来调用相应的派生类函数,实现多态性。这是面向对象编程中的一个核心概念。

(2)通过基类指针或引用访问派生类的成员函数:使用基类指针或引用来访问派生类的成员函数,可以简化代码,并提高代码的可扩展性和灵活性。通过虚函数,可以以统一的方式操作不同类型的对象,而不需要根据对象的具体类型编写大量的条件语句。

(3)在继承关系中覆盖基类函数:派生类可以通过重写继承的虚函数来覆盖基类的实现。这使得派生类可以根据自身的需要来修改继承的行为,从而实现定制化和特殊化的功能。这是面向对象编程的继承和多态性的基本特性之一。

通过使用虚函数,可以实现动态的函数调用和多态性,使得程序更加灵活和可扩展。虚函数是C++中实现多态性的重要机制之一,它是面向对象编程的核心概念之一。

(2)虚函数的声明和定义

虚函数的声明和定义步骤如下:

【1】在基类中使用virtual关键字声明虚函数。

【2】在派生类中重写基类的虚函数。

以下是一个最简单的例子,演示了虚函数的声明和定义:

#include <iostream>

class Base {

public:

    virtual void display() {

        std::cout << “This is the Base class” << std::endl;

    }

};

class Derived : public Base {

public:

    void display() {

        std::cout << “This is the Derived class” << std::endl;

    }

};

int main() {

    Base* basePtr;

    Base baseObj;

    Derived derivedObj;

    basePtr = &baseObj;

    basePtr->display(); // 调用 Base 类的 display 函数

    basePtr = &derivedObj;

    basePtr->display(); // 调用 Derived 类的 display 函数

    return 0;

}

在这个例子中,我们定义了一个基类Base和一个派生类Derived。Base类中有一个虚函数display(),Derived类重写了这个虚函数。

在main()函数中,我们创建了一个基类指针basePtr并分别指向基类对象和派生类对象。通过使用基类指针调用`display()`函数,我们可以看到根据对象的实际类型,程序会动态地调用相应的派生类中的函数。

输出结果为:

“`

This is the Base class

This is the Derived class

“`

可以看到,通过虚函数的使用,程序根据对象的实际类型,动态地调用了正确的派生类中的函数实现。

(3)虚函数的访问

【1】对象名访问

class Base {

  public:

    virtual void display() {

        cout << “This is the Base class” << endl;

    }

};

class Child : public Base {

  public:

    void display() {

        cout << “This is the child class” << endl;

    }

};

int main() {

    //创建基类的对象obj_base

    Base obj_base;

    //创建派生类的对象obj_child

    Child obj_child;

    //通过对象名调用虚函数

    obj_base.display();

    //通过对象名调用虚函数

    obj_child.display();

    //通过类名加作用域限定符指明要调用的方法

    obj_base.Base::display();

    //通过类名加作用域限定符指明要调用的方法

    obj_child.Child::display();

    return 0;

}

【2】基指针访问

基指针访问指的是使用基指针分别指向派生类的地址,然后调用虚函数,这里涉及基指针转换规则的问题,转换规则如下。

1)指向基类的指针,可以指向它的公有派生的对象,但不能指向私有派生的对象。

2)只能利用它直接访问派生类从基类继承来的成员,不能直接访问公有派生类中特定的成

3)不能将指向派生类对象的指针指向其基类的一个对象。

class Base {

  public:

    virtual void display() {

        cout << “This is the Base class” << endl;

    }

};

class Child : public Base {

  public:

    void display() {

        cout << “This is the child class” << endl;

    }

};

int main() {

    //创建基类的对象obj_base;使用基类对象地址为基类指针赋值

    Base obj_base;

    Base* pBase = &obj_base;

    //创建派生类的对象obj_child;使用派生类对象地址为派生类指针赋值

    Child obj_child;

    Child* pChild = &obj_child;

    pBase->display();

    pChild->display();

    //将派生类指针赋值给基类指针

    pBase = pChild;

    pBase->display();

    //使用基类对象地址为派生类指针赋值

    pChild = (Child*)&obj_base;

    pChild->display();

    pChild->Base::display();

    return 0;

}

【3】引用访问

使用引用访问虚函数与使用指针访问虚函数类似,不同的是引用已经声明,不能修改,因此在使用上有一定限制,但这在一定程度上提高了代码的安全性,特别是在函数参数传递等场合中,可以将引用理解成一种“受限制的指针”,如下列代码所示。

class Base {

  public:

    virtual void display() {

        cout << “This is the Base class” << endl;

    }

};

class Child : public Base {

  public:

    void display() {

        cout << “This is the child class” << endl;

    }

};

int main() {

    //创建基类的对象obj_base

    Base obj_base;

    //创建派生类的对象obj_child

    Child obj_child;

    //声明基类的引用,用基类对象初始化

    Base& rBase1 = obj_base;

    rBase1.display();

    //声明基类的引用,用派生类对象初始化

    Base& rBase2 = obj_child;

    rBase2.display();

    return 0;

}

【4】类内访问

在类内的成员函数中访问该类层次中的虚函数,要使用this指针,如下所示。

class Base {

  public:

    virtual void display() {

        cout << “This is the Base class” << endl;

    }

    //使用this或者直接访问

    void call_base_1(){

        this->display();

    }

    void call_base_2(){

        Base::display();

    }

};

class Child : public Base {

  public:

    //对虚函数进行覆盖

    void display() {

        cout << “This is the child class” << endl;

    }

    void call_child_1(){

        display();

    }

};

int main() {

    Base obj_base;

    Child obj_child;

    obj_base.call_base_1();

    obj_child.call_child_1();

    Base* pBase = &obj_base;

    pBase->call_base_1();

    pBase->call_base_2();

    pBase = &obj_child;

    pBase->call_base_1();

    pBase->call_base_2();

    return 0;

}

3、纯虚函数

C++中的纯虚函数是在基类中声明但没有提供实现的虚函数。纯虚函数通过使用“= 0”来声明。由于纯虚函数没有具体的实现,所以不能直接创建基类的对象,而只能在派生类中提供具体的实现。纯虚函数一般定义形式如下所示:

class   类名{

  virtual  类型  函数名(参数表) = 0;

   …

}

以下是一个关于电脑的例子,演示了纯虚函数的声明和使用:

#include <iostream>

class Computer {

public:

    virtual void turnOn() = 0; // 纯虚函数,没有具体实现

    virtual void turnOff() = 0; // 纯虚函数,没有具体实现

};

class Desktop : public Computer {

public:

    void turnOn() override {

        cout << “Desktop is turning on…” << endl;

    }

    void turnOff() override {

        cout << “Desktop is turning off…” << endl;

    }

};

class Laptop : public Computer {

public:

    void turnOn() override {

        cout << “Laptop is turning on…” << endl;

    }

    void turnOff() override {

        cout << “Laptop is turning off…” << endl;

    }

};

int main() {

    Computer* computer;

    Desktop desktop;

    Laptop laptop;

    computer = &desktop;

    computer->turnOn(); // 调用 Desktop 类的 turnOn 函数

    computer->turnOff(); // 调用 Desktop 类的 turnOff 函数

    computer = &laptop;

    computer->turnOn(); // 调用 Laptop 类的 turnOn 函数

    computer->turnOff(); // 调用 Laptop 类的 turnOff 函数

    return 0;

}

在这个例子中,我们定义了一个基类Computer和两个派生类Desktop和Laptop。Computer类中声明了两个纯虚函数turnOn()和turnOff(),并没有提供具体的实现。派生类Desktop和Laptop分别重写了这两个纯虚函数,提供了具体的实现。

在main()函数中,我们创建了一个基类指针computer并分别指向派生类对象desktop和laptop。通过使用基类指针调用turnOn()和turnOff()函数,我们可以看到根据对象的实际类型,程序会动态地调用相应的派生类中的函数实现。

输出结果为:

Desktop is turning on…

Desktop is turning off…

Laptop is turning on…

Laptop is turning off…

可以看到,通过纯虚函数的使用,我们可以在基类中声明接口,而将具体的实现推迟到派生类中。纯虚函数在面向对象设计中具有重要的作用,可以实现接口和抽象类的概念,并通过派生类提供具体的实现。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容