1. 简介

多态按字面的意思就是多种形态。当类与类之间存在继承关系的时候,就会用到多态。

C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。多态分为为静态多态动态多态 两种。

平常说的多态是 动态多态

2. 静态多态

静态多态是编译器在编译期间完成的,编译器会根据实参类型来选择调用合适的函数,如果有合适的函数可以调用就调,没有的话就会发出警告或者报错 。 该种方式的出现有两处地方: 函数重载泛型编程 | 函数模板

  • 特点
  • 使用场景
  1. 函数重载(Function Overloading): 在同一个作用域内定义了多个同名但参数列表不同的函数。根据函数调用时传递的参数类型或数量,编译器会决定调用哪个具体的函数。
#include <iostream>

void print(int num) {
    std::cout << "Integer: " << num << std::endl;
}

void print(double num) {
    std::cout << "Double: " << num << std::endl;
}

int main() {
    int a = 20;
    double b = 3.14;

    print(a);  // 调用print(int)
    print(b);  // 调用print(double)

    return 0;
}
  1. 模板(Template): 使用模板可以编写泛型代码,在编译时生成对应特定类型的代码,从而实现静态多态。
#include <iostream>

template<typename T>
void print(T value) {
    std::cout << "Value: " << value << std::endl;
}

int main() {
    int a = 20;
    double b = 3.14;

    print(a);  // 根据实参类型生成print<int>(int)
    print(b);  // 根据实参类型生成print<double>(double)

    return 0;
}

3. 动态多态

动态多态: 指只有在运行的时候才能决定到底调用哪个类的函数

动态多态的必须满足两个条件:

代码:

#include <iostream>

using namespace std;

class father{
public:
    void doSomething(){
        cout << "父亲在做事..." <<endl;
    }
};
class son : public father{
public:
    void doSomething(){
        cout << "儿子在干活..." <<endl;
    }
};
int main() {

    //father &f0 = father f0;   报错,'father' does not refer to a value
    //这是父类的对象
    father f;
    f.doSomething();
    //父类的引用能接受父类的对象
    father &f1 = f;
    f1.doSomething();
    //父类的指针接收父类的对象。
    father * f2  =new father();
    f2->doSomething();
    //子类的指针接收子类的对象
    son * s = new son();
    s->doSomething();
    //父类的指针接收子类对象
    father *f4 = new son();
    f4->doSomething();

    // son * s1 =new father(); 报错 Cannot initialize a variable of type 'son *' with an rvalue of type 'father *'

    return 0;
}

运行结果:

父亲在做事...
父亲在做事...
父亲在做事...
儿子在干活...
父亲在做事...
  • 特点
  • 使用场景
  1. 多态行为:当有多个派生类对象,但是希望以一种统一的方式处理它们时,动态多态性非常有用。通过使用基类的指针或引用,可以在运行时根据实际对象的类型来选择调用的函数,实现多态行为。
#include <iostream>

// 基类 Shape
class Shape {
public:
    virtual double area() const = 0;
};

// 派生类 Rectangle
class Rectangle : public Shape {
private:
    double width;
    double height;

public:
    Rectangle(double w, double h) : width(w), height(h) {}

    double area() const override {
        return width * height;
    }
};

// 派生类 Circle
class Circle : public Shape {
private:
    double radius;

public:
    Circle(double r) : radius(r) {}

    double area() const override {
        return 3.14159 * radius * radius;
    }
};

int main() {
    Shape* shape1 = new Rectangle(4, 3);
    Shape* shape2 = new Circle(5.0);

    std::cout << "Rectangle area: " << shape1->area() << std::endl;
    std::cout << "Circle area: " << shape2->area() << std::endl;

    delete shape1;
    delete shape2;

    return 0;
}
  1. 统一接口:通过使用基类的指针或引用,可以定义一个通用的接口,处理一组派生类对象。这样可以在不了解具体派生类的情况下,统一地访问它们的接口,提供代码的灵活性和可维护性。
#include <iostream>

// 基类 Drawable
class Drawable {
public:
    virtual void draw() const = 0;
};

// 派生类 Rectangle
class Rectangle : public Drawable {
public:
    void draw() const override {
        std::cout << "Drawing a rectangle." << std::endl;
    }
};

// 派生类 Circle
class Circle : public Drawable {
public:
    void draw() const override {
        std::cout << "Drawing a circle." << std::endl;
    }
};

void drawShapes(const Drawable& shape) {
    shape.draw();
}

int main() {
    Rectangle rectangle;
    Circle circle;

    drawShapes(rectangle);
    drawShapes(circle);

    return 0;
}
  1. 扩展性:通过继承和虚函数,动态多态性提供了一种灵活的方式来扩展代码。当需要添加新的派生类时,只需继承基类并重写虚函数即可,而不需要修改已有的代码。
#include <iostream>

// 基类 Animal
class Animal {
public:
    virtual void sound() const {
        std::cout << "Animal makes sound." << std::endl;
    }
};

// 派生类 Dog
class Dog : public Animal {
public:
    void sound() const override {
        std::cout << "Dog barks." << std::endl;
    }
};

// 派生类 Cat
class Cat : public Animal {
public:
    void sound() const override {
        std::cout << "Cat meows." << std::endl;
    }
};

// 扩增派生类 Bird
class Bird : public Animal {
public:
    void sound() const override {
        std::cout << "Bird sings." << std::endl;
    }
};

int main() {
    Animal* animal1 = new Dog();
    Animal* animal2 = new Cat();
    Animal* animal3 = new Bird();

    animal1->sound();
    animal2->sound();
    animal3->sound();

    delete animal1;
    delete animal2;
    delete animal3;

    return 0;
}
  • 注意点

4. 虚函数

C++中的虚函数的作用主要是实现了多态的机制 , 有了虚函数就可以在父类的指针或者引用指向子类的实例的前提下,然后通过父类的指针或者引用调用实际子类的成员函数。这时父类的指针或引用具备了多种形态。定义虚函数:在函数声明前,加上 virtual 关键字即可。 在父类的函数上添加 virtual 关键字,可使子类的同名函数也变成虚函数。
如果基类指针指向的是一个基类对象,则基类的虚函数被调用 ,如果基类指针指向的是一个派生类对象,则派生类的虚函数被调用。

  • 特点

4.1 工作原理

c++ 多态-LMLPHP

4.2 构造函数可以是虚函数吗?

构造函数不能为虚函数 , 因为虚函数的调用,需要虚函数表(指针),而该指针存在于对象开辟的空间中,而对象的空间开辟依赖构造函数的执行,这就矛盾了。

#include <iostream>

using namespace std;

class father{
public:
    // virtual father(){}   Constructor cannot be declared 'virtual'
    father(){
        cout << "父类的构造..." <<endl;
    }
};

class son : public father{
public:
    son(){
        cout << "子类的构造..." <<endl;
    }
};

int main() {

    son s;
    
    return 0;
}

4.3 析构函数可以是虚函数吗?


using namespace std;

class father{
public:
    father(){
        cout << "父类的构造..." <<endl;
    }

    virtual ~father(){
        cout << "父类的析构..." <<endl;
    }
};

class son : public father{
public:
    son(){
        cout << "子类的构造..." <<endl;
    }
    ~son(){
        cout << "子类的析构..." <<endl;
    }
};

int main() {

    father * f = new son ();
    delete f;

    return 0;
}

5. 纯虚函数

纯虚函数是一种特殊的虚函数,C++中包含纯虚函数的类,被称为是“抽象类”。抽象类不能使用new出对象,只有实现了这个纯虚函数的子类才能new出对象。C++中的纯虚函数更像是“只提供声明,没有实现”,是对子类的约束。
纯虚函数就是没有函数体,同时在定义的时候,其函数名后面要加上“= 0”
代码:

#include <iostream>

using namespace std;


class Animal{
public:
    // 动物类的吃的行为,看起来更像是对子类的一种抽象,或者是看起来像是一个功能的声明。
    virtual void eat() = 0 ;

    /*virtual void eat(){
        cout << "动物在吃..." <<endl;
    }*/
};

class Bear : public Animal{
public:
    void eat(){
        cout << "熊吃鱼..." <<endl;
    }
};

class Tiger : public Animal{
public:
    void eat(){
        cout << "老虎吃肉..." <<endl;
    }
};

class Pangolin : public Animal{
public:
    void eat(){
        cout << "穿山甲吃蚂蚁..." <<endl;
    }
};

class Suckler : public Animal{};


int main() {

    //Animal S = new Suckler;  报错, Allocating an object of abstract class type 'Suckler'

    Animal * B = new Bear;
    B->eat();

    Animal * T = new Tiger;
    T->eat();

    Animal * P = new Pangolin;
    P->eat();


    return 0;
}
  • 注意点
10-19 05:46