C++ · 代码笔记5 · 探索多态与虚函数

目录

前言

本笔记所涉及到的编程环境与 《C++ · 代码笔记1 · 从C到C++》 中的编程环境一致,具体可参考此笔记。

011虚函数_使用基类指针实现覆盖特性

相关代码:

c 复制代码
#include <iostream>
using namespace std;

class Base
{
public:
    virtual void show()
    {
        cout << "Base show" << endl;
    }
};

class Derived : public Base
{
public:
    // 使用 override 关键字明确表示覆盖
    void show() override
    {
        cout << "Derived show" << endl;
    }
};

int main()
{
    // 基类指针调用基类函数
    Base *bPtr = new Base;
    bPtr->show();

    // 派生类指针调用派生类函数
    Derived *dPtr = new Derived();
    dPtr->show();

    // 基类指针指向派生类对象,基类show函数已经被声明为了虚函数,此时会表现出多态性。
    // 当基类相关函数声明为虚函数时,派生类相关函数将会覆盖基类相关函数,
    // 此时会根据基类指针指向的对象类型调用相关函数
    bPtr = dPtr;
    bPtr->show();

    return 0;
}

运行结果:

012虚函数_使用引用实现覆盖特性

相关代码:

c 复制代码
#include <iostream>
using namespace std;

class Base
{
public:
    virtual void show()
    {
        cout << "Base show" << endl;
    }
};

class Derived : public Base
{
public:
    // 使用 override 关键字明确表示覆盖
    void show() override
    {
        cout << "Derived show" << endl;
    }
};

int main(int argc, char const *argv[])
{
    Base b_obj;
    Base &br = b_obj;
    br.show();

    Derived d_obj;
    Derived &dr = d_obj;
    dr.show();

    // 基类引用派生类对象,此时由于基类的函数被声明为了虚函数,表现出了多态性,
    // 所以此时调用的是派生类的函数
    Base &brd = d_obj;
    brd.show();

    return 0;
}

运行结果:

013使用多态场景小例程

相关代码:

c 复制代码
#include <iostream>
using namespace std;

/**
 * 这个例子中就体现出了多态的优点,如果使用非多态的方式实现这个程序,
 * 那么将会需要定义很多指针,但使用了多态仅使用一个指针变量即可。
 */

// 动物类
class Animal
{
public:
    virtual void makeSound() const
    {
        cout << "动物发出声音!" << endl;
    }
    virtual ~Animal() {}
};

// 狗类
class Dog : public Animal
{
public:
    void makeSound() const override
    {
        cout << "--狗狗:汪汪!" << endl;
    }
};

// 猫类
class Cat : public Animal
{
public:
    void makeSound() const override
    {
        cout << "--猫咪:喵喵!" << endl;
    }
};

// 鸟类
class Bird : public Animal
{
public:
    void makeSound() const override
    {
        cout << "--小鸟:啾啾!" << endl;
    }
};

// 大象类
class Elephant : public Animal
{
public:
    void makeSound() const override
    {
        cout << "--大象:哞哞!" << endl;
    }
};

int main()
{
    Animal *p = new Animal;
    p->makeSound();

    // 创建不同类型的动物
    p = new Dog;
    p->makeSound();
    p = new Cat;
    p->makeSound();
    p = new Bird;
    p->makeSound();
    p = new Elephant;
    p->makeSound();

    // 释放内存
    delete p;

    return 0;
}

运行结果:

020构成多态的条件

相关代码:

c 复制代码
#include <iostream>
using namespace std;

/**
 * 构成多态条件:
 *  1、必须存在继承关系;
 *  2、继承关系中必须有同名的虚函数,并且它们是覆盖关系(函数原型相同)。
 *  3、存在基类的指针,通过该指针调用虚函数。
 */

class Base
{
public:
    virtual void func();
    virtual void func(int);
    void func(double);
};
void Base::func()
{
    cout << "void Base::func()" << endl;
}
void Base::func(int n)
{
    cout << "void Base::func(int)" << endl;
}
void Base::func(double n)
{
    cout << "void Base::func(double)" << endl;
}

class Derived : public Base
{
public:
    void func();
    void func(char *);
    void func(double);
};
void Derived::func()
{
    cout << "void Derived::func()" << endl;
}
void Derived::func(char *str)
{
    cout << "void Derived::func(char *)" << endl;
}
void Derived::func(double n)
{
    cout << "void Derived::func(double)" << endl;
}

/** 本例是一个简单的多态例程。以本例来说,当一个基类指针指向派生类对象时,
 * 会调用的函数有以下三种情况:
 *   1、派生类【重写】了基类的虚函数,在调用处传递完全符合【重写】函数的形参列表参数时,
 *       调用派生类重写的函数,如下面的 a 注释处的代码
 *   2、派生类【未重写】基类的虚函数,在调用处传递完全符合【未重写】函数的形参列表参数时,
 *       调用基类的虚函数,如下面的b注释处的代码
 *   3、仅派生类有该函数,基类没有相关的虚函数,则不能调用,否则会编译报错,
 *       如下面c注释处的代码
 *   4、补充内容:当基类成员函数未声明为虚函数,即使是派生类对其进行了隐藏,
 *       但调用的仍会是基类的函数,如注释d处代码所示。
 **/
int main(int argc, char const *argv[])
{
    Base *p = new Derived();
    // a、调用的是派生类的虚函数,构成了多态。
    p->func();
    // b、调用的是基类的虚函数,因为派生类中没有函数覆盖它。
    p->func(10);
    // c、编译报错,因为通过基类的指针只能访问从基类继承过去的成员,不能访问派生类新增的成员。
    // p->func("test");
    // p指针是基类指针且基类函数未被声明为virtual,这将会调用基类成员函数
    p->func(1.0);

    return 0;
}

运行结果:

030虚析构函数

相关代码:

c 复制代码
#include <iostream>

/**
 *  基类的析构函数如果不声明为虚析构函数,那么当基类指针指向派生类对象的情况下
 * 销毁对象仅调用基类的析构函数,不会调用派生类的析构函数,
 * 这会导致派生类中的资源无法得到释放,产生内存泄漏的危险
 **/

class Base
{
public:
    Base()
    {
        std::cout << "基类构造函数被调用" << std::endl;
    }

    virtual ~Base()
    {
        std::cout << "基类析构函数被调用" << std::endl;
    }
};

class Derived : public Base
{
public:
    Derived()
    {
        std::cout << "派生类构造函数被调用" << std::endl;
    }

    ~Derived() override
    {
        std::cout << "派生类析构函数被调用" << std::endl;
    }
};

int main()
{
    Base *obj = new Derived();

    delete obj;

    return 0;
}

运行结果:

040纯虚函数

相关代码:

c 复制代码
#include <iostream>

/**
 * 1、仅能在类中定义纯虚函数
 * 2、纯虚函数必须要在派生类中定义,且全部完成实现
 */

class A
{
public:
    // 定义纯虚函数vFunc1,没有函数体,需要在派生类中实现
    virtual void vFunc1(void) = 0;
    // 定义纯虚函数vFunc2,没有函数体,需要在派生类中实现
    virtual void vFunc2(void) = 0;
};

class B : public A
{
public:
    // 声明B类对A类的vFunc1函数进行覆盖实现
    void vFunc1(void) override;
};
// B类对A类的vFunc1函数的具体实现
void B::vFunc1(void)
{
    std::cout << "派生类 B类 实现了 抽象基类的vFunc1函数" << std::endl;
}

class C : public B
{
public:
    // 声明C类对A类的vFunc2函数进行覆盖实现
    void vFunc2(void) override;
};
// C类对A类的vFunc2函数的具体实现
void C::vFunc2(void)
{
    std::cout << "派生类 C类 实现了 抽象基类的vFunc2函数" << std::endl;
}

int main(int argc, char const *argv[])
{
    C c_obj;

    A *pa = &c_obj;

    // 通过基类指针调用派生类实现的vFunc1函数
    pa->vFunc1();
    // 通过基类指针调用派生类实现的vFunc2函数
    pa->vFunc2();

    return 0;
}

运行结果:

051typeinfo运算符_获取类型信息

相关代码:

c 复制代码
#include <iostream>
#include <typeinfo>

// 定义一个简单的类
class MyClass
{
public:
    MyClass() {}
};

// 定义一个简单的结构体
struct MyStruct
{
    int member;
};

int main()
{
    int i = 10;
    double d = 10.5;
    char c = 'a';
    std::string s = "Hello";
    MyClass obj;
    struct MyStruct struct_obj;

    // 获取和打印不同类型的类型信息
    std::cout << "typeid(1).name(): " << typeid(1).name() << std::endl;
    std::cout << "typeid(1.0).name(): " << typeid(1.0).name() << std::endl;
    std::cout << "typeid(int).name(): " << typeid(i).name() << std::endl;
    std::cout << "typeid(double).name(): " << typeid(d).name() << std::endl;
    std::cout << "typeid(char).name(): " << typeid(c).name() << std::endl;
    std::cout << "typeid(std::string).name(): " << typeid(s).name() << std::endl;
    std::cout << "typeid(MyClass).name(): " << typeid(obj).name() << std::endl;
    std::cout << "typeid(MyStruct).name(): " << typeid(struct_obj).name() << std::endl;


    // 比较两个类型是否相同
    if (typeid(obj) == typeid(MyClass))
    {
        std::cout << "obj is of type MyClass" << std::endl;
    }

    if (typeid(struct_obj) == typeid(MyStruct))
    {
        std::cout << "struct_obj is of type MyStruct" << std::endl;
    }

    return 0;
}

运行结果:

052typeinfo_根据不同类型进行不同操作

相关代码:

c 复制代码
#include <iostream>
#include <typeinfo>
using namespace std;

// 基类
class Animal
{
public:
    virtual void speak() const
    {
        cout << "我是一只动物。" << endl;
    }
    virtual ~Animal(){}; // 将析构函数声明为虚的
};

// 派生类
class Dog : public Animal
{
public:
    void speak() const override
    {
        cout << "我是一只狗。汪汪!" << endl;
    }
    ~Dog() {}
};

// 另一个派生类
class Cat : public Animal
{
public:
    void speak() const override
    {
        cout << "我是一只猫。喵喵!" << endl;
    }
    ~Cat() {}
};

int main(int argc, char const *argv[])
{
    Animal *animal;
    int choice;

    cout << "输入一个数字(1 表示狗,2 表示猫): ";
    cin >> choice;

    if (choice == 1)
    {
        animal = new Dog();
    }
    else if (choice == 2)
    {
        animal = new Cat();
    }
    else
    {
        animal = new Animal();
    }

    // 使用 typeid 进行类型检查
    if (typeid(*animal) == typeid(Dog))
    {
        cout << "这只动物是一只狗。" << endl;
    }
    else if (typeid(*animal) == typeid(Cat))
    {
        cout << "这只动物是一只猫。" << endl;
    }
    else
    {
        cout << "这只动物是一只普通的动物。" << endl;
    }

    // 清理动态分配的内存
    delete animal;

    return 0;
}

运行结果:

相关推荐
小码农<^_^>2 分钟前
c++继承(下)
开发语言·c++
盒马盒马16 分钟前
Redis:cpp.redis++通用接口
数据库·c++·redis
LearnTech_12321 分钟前
【学习笔记】手写一个简单的 Spring MVC
笔记·学习·spring·spring mvc
无夜_1 小时前
Prototype(原型模式)
开发语言·c++
笑鸿的学习笔记1 小时前
工具笔记之生成图表和可视化的标记语言Mermaid
笔记
刘好念2 小时前
[图形学]smallpt代码详解(1)
c++·计算机图形学
fpcc2 小时前
并行编程实战——TBB框架的应用之一Supra的基础
c++·并行编程
兵哥工控2 小时前
MFC工控项目实例二十二主界面计数背景颜色改变
c++·mfc
兵哥工控2 小时前
MFC工控项目实例二十手动测试界面模拟量输入实时显示
c++·mfc
kissSimple2 小时前
UE行为树编辑器图文笔记
笔记·ue5·编辑器·unreal engine·unreal engine 5