C++ 面向对象 多态(虚函数和纯虚函数)

C++ 的面向对象编程(OOP)中的多态(Polymorphism)是指同一个操作可以作用于不同类型的对象。多态可以分为两种类型:编译时多态(静态多态)和运行时多态(动态多态)。多态是C++面向对象编程的四大特性之一,其他三个特性分别是抽象、封装和继承。多态性是程序在运行时可以决定调用哪个函数,从而实现更加灵活和可扩展的代码。本文主要介绍C++ 面向对象 多态,以及虚函数和纯虚函数。

1、编译时多态(静态多态)

编译时多态主要通过函数重载(Function Overloading)和运算符重载(Operator Overloading)实现。

1)函数重载(Function Overloading)

函数重载是指在同一个作用域中,可以定义多个函数名相同但参数不同的函数。

#include <iostream>
using namespace std;

class Printer {
public:
    void print(int i) {
        cout << "Integer: " << i << endl;
    }

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

    void print(const string &str) {
        cout << "String: " << str << endl;
    }
};

int main() {
    Printer p;
    p.print(10);        // Integer: 10
    p.print(3.14);      // Double: 3.14
    p.print("Hello");   // String: Hello
    return 0;
}

2)运算符重载(Operator Overloading)

C++ 开发者可以重载标准运算符,使其能用于用户自定义类型。

#include <iostream>
using namespace std;

class Complex {
private:
    float real;
    float imag;
public:
    Complex() : real(0), imag(0) {}

    Complex operator + (const Complex& other) {
        Complex temp;
        temp.real = real + other.real;
        temp.imag = imag + other.imag;
        return temp;
    }

    void display() {
        cout << real << " + " << imag << "i" << endl;
    }
};

int main() {
    Complex c1, c2, c3;
    c3 = c1 + c2;
    c3.display();   // 0 + 0i
    return 0;
}

2、运行时多态(动态多态)

运行时多态是通过继承虚函数(Virtual Functions)来实现的。当基类指针或引用指向派生类对象时,可以调用派生类的重写方法,而不是基类的方法。

1)虚函数

虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。

我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定。派生类重写基类中的虚函数。

#include <iostream>
using namespace std;

class Animal {
public:
    // 声明虚函数
    virtual void sound() {
        cout << "Animal makes a sound" << endl;
    }
    // 虚析构函数,用于确保派生类对象销毁时能正确调用析构函数
    virtual ~Animal() {
        cout << "Animal destroyed" << endl;
    }
};

class Dog : public Animal {
public:
    // 重写虚函数
    void sound() override {
        cout << "Bark" << endl;
    }
    // 派生类的析构函数
    ~Dog() {
        cout << "Dog destroyed" << endl;
    }
};

int main() {
    // 基类指针指向派生类对象
    Animal* animal1 = new Dog();
    // 调用虚函数时,根据对象的实际类型
    // 决定调用哪个版本的函数
    animal1->sound();  // Bark

    // 删除对象时,确保正确调用派生类的析构函数
    delete animal1;

    return 0;
}

2)基类指针或引用

通过基类指针或引用调用虚函数时,实际调用的是派生类的实现(如果派生类重写了该虚函数)。

#include <iostream>
using namespace std;

class Animal {
public:
    virtual void sound() {  // 虚函数
        cout << "Animal sound" << endl;
    }
};

class Dog : public Animal {
public:
    void sound() override {  // 重写基类的虚函数
        cout << "Bark" << endl;
    }
};

class Cat : public Animal {
public:
    void sound() override {  // 重写基类的虚函数
        cout << "Meow" << endl;
    }
};

int main() {
    Animal* animal1 = new Dog();  // 基类指针指向派生类对象
    Animal* animal2 = new Cat();  // 基类指针指向派生类对象

    animal1->sound();  // Bark
    animal2->sound();  // Meow

    delete animal1;
    delete animal2;

    return 0;
}

3)纯虚函数

可能想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。

例如,

#include <iostream>
using namespace std;
class Shape {
   protected:
      int width, height;
   public:
      Shape( int a=0, int b=0)
      {
         width = a;
         height = b;
      }
      // pure virtual function
      virtual int area() = 0;
};
int main() {

  cout << "Shape 是抽象类";

  return 0;

}

4)多态使用示例

#include <iostream>
using namespace std;
// 基类
class Animal {
  public:
    void say() {
    cout << "hello! \n" ;
  }
};
// 派生类
class Pig : public Animal {
  public:
    void say() {
    cout << "hi \n" ;
   }
};
// 派生类
class Dog : public Animal {
  public:
    void say() {
    cout << "cjavapy \n" ;
  }
};
int main() {
  Animal myAnimal;
  Pig myPig;
  Dog myDog;
  myAnimal.say();
  myPig.say();
  myDog.say();
  return 0;
}
推荐阅读
cjavapy编程之路首页