实现多态
本文的目的是为了实现C++的多态调用,为此,我们需要先了解一些知识,后续来一个具体实现。
虚函数与纯虚函数的区别
在C++中,虚函数(virtual function)和纯虚函数(pure virtual function)是面向对象编程中实现多态性的关键概念。它们之间的主要区别在于声明、实现和使用方面。下面详细解释这两种虚函数的特点和用途:
虚函数(Virtual Function)
虚函数是在基类中声明的,其主要目的是为了在派生类中可以被重写,从而实现运行时的多态性。当一个对象指针或引用指向派生类对象时,通过该指针或引用调用虚函数,会调用到派生类中重写的那个函数,而不是基类中的函数。这是C++多态的基础。
声明:
虚函数的声明使用virtual
关键字前缀,例如:
class Base {
public:
virtual void func() { /* ... */ }
};
实现:
虚函数在基类中可以有默认实现,派生类可以选择性地重写这个函数。
作用:
- 实现运行时多态性。
- 可以在基类中提供默认的行为,派生类按需覆盖。
纯虚函数(Pure Virtual Function)
纯虚函数是一种特殊的虚函数,它在基类中没有实现,必须在派生类中被实现。纯虚函数的存在是为了强制派生类必须提供一个具体的实现,否则编译器将报错。
声明:
纯虚函数的声明同样使用virtual
关键字,但是函数体用= 0
表示,例如:
class Base {
public:
virtual void func() = 0; // 纯虚函数
};
实现:
纯虚函数在基类中没有实现,派生类必须提供实现,否则派生类也将变成抽象类,不能实例化。
作用:
- 强制派生类实现某个方法,确保所有派生类都有相同的行为接口。
- 基类带有纯虚函数时,基类就变成了抽象类,不能被实例化,只能作为其他类的基类。
总结
- 虚函数允许在基类中有默认实现,可以被派生类重写或保留原样,主要用于实现多态性。
- 纯虚函数在基类中无实现,强制派生类必须实现,常用于定义接口,确保所有派生类都提供一致的行为。
通过使用虚函数和纯虚函数,C++支持了面向对象编程中的封装、继承和多态特性,提高了代码的复用性和灵活性。
好了,前面这个是前提,就是如何定义一个抽象类,子类实现具体代码的知识点了,现在来写一个抽象类:
动物抽象类
Animal.hpp
#pragma once
//动物抽象类
class Animal {
public:
virtual ~Animal() {}
virtual std::string run() const = 0; // 纯虚函数
};
子类
Cat.hpp
#pragma once
#include <iostream>
#include "Animal.hpp"
//实现类猫
class Cat : public Animal {
public:
Cat() {
std::cout << "Cat 构造";
}
std::string run() const override {
return "喵喵喵";
}
};
Dog.hpp
#pragma once
#include <iostream>
#include "Animal.hpp"
//实现类狗
class Dog : public Animal {
public:
Dog() {
std::cout << "Dog 构造";
}
std::string run() const override {
return "汪汪汪";
}
};
好了,类有了,现在就是实现多态的时候了,弄一个动物工厂,用来创建需要的类
AnimalFactory2.hpp
#pragma once
#include <memory>
#include "Animal.hpp"
#include "Dog.hpp"
#include "Cat.hpp"
//动物工厂
class AnimalFactory2 {
public:
static std::unique_ptr<Animal> createAnimal(const std::string& animalType) {
if (animalType == "Cat") {
return std::make_unique<Cat>();
}
if (animalType == "Dog") {
return std::make_unique<Dog>();
}
throw std::runtime_error("Invalid animal type");
}
};
使用方式如下:
try
{
auto dog = AnimalFactory2::createAnimal("Dog");
std::cout << dog->run() << std::endl;
auto cat = AnimalFactory2::createAnimal("Cat");
std::cout << cat->run() << std::endl;
}
catch (const std::exception& e)
{
std::cerr << e.what() << std::endl;
}
这里就实现了面向对象的多态了,简单有效,但是有点不足的是,扩展性,新增一种动物需要修改工厂,我们来改改,换一种工厂
AnimalFactory.hpp
#pragma once
#include <functional>
#include <map>
#include <memory>
#include <stdexcept>
#include <string>
#include "Animal.hpp"
//动物工厂
class AnimalFactory {
private:
static std::map<std::string, std::function<std::unique_ptr<Animal>()>> animalCreators;
public:
static void registerAnimal(const std::string& animalType,
std::function<std::unique_ptr<Animal>()> creator) {
animalCreators[animalType] = creator;
}
//创建函数,通过映射查找并创建相应的对象
static std::unique_ptr<Animal> createAnimal(const std::string& animalType) {
auto it = animalCreators.find(animalType);
if (it != animalCreators.end()) {
return it->second();
}
else {
throw std::runtime_error("Invalid animal type");
}
}
};
// 初始化静态成员
std::map<std::string, std::function<std::unique_ptr<Animal>()>> AnimalFactory::animalCreators;
利用 std::map 和 std::function ,lambda 实现一种字符串与对象的映射关系,使用方式如下:
AnimalFactory::registerAnimal("Dog", []() { return std::make_unique<Dog>(); });
AnimalFactory::registerAnimal("Cat", []() { return std::make_unique<Cat>(); });
try {
auto dog = AnimalFactory::createAnimal("Dog");
std::cout << dog->run() << std::endl;
auto cat = AnimalFactory::createAnimal("Cat");
std::cout << cat->run() << std::endl;
}
catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
这种方式,实现略显复杂,但是扩展性好,对于需要实现的类,注册到工厂里,后续使用方便。