Skip to content

C++课程 第12讲:类型转换、多态与虚函数

一、类型转换 (Type Conversion)

1. 显示转换 (Explicit Conversion)

  • C风格类型转换

    • 改进前: 试图将一个字符串转换为整数,可能导致数据丢失或程序崩溃

      c++
      #include <iostream>
      using namespace std;
      
      int main() {
        const char* str = "123";
        int a = int(str); // 错误!不能直接将字符串指针转换为 int
        cout << "a = " << a << endl;
        return 0;
      }
    • 改进后: 使用 std::stoi 函数将字符串转换为整数

      c++
      #include <iostream>
      #include <string>
      using namespace std;
      
      int main() {
        string str = "123";
        int a = std::stoi(str); 
        cout << "a = " << a << endl;
        return 0;
      }
  • static_cast (C++风格类型转换)

    • 改进前: 试图将一个基类指针转换为派生类指针,如果没有进行动态类型转换可能会导致未定义行为

      c++
      #include <iostream>
      using namespace std;
      
      class Base {};
      class Derived : public Base {};
      
      int main() {
        Base* b = new Base();
        Derived* d = static_cast<Derived*>(b); // 错误!b 实际指向 Base 对象
        delete d; // 未定义行为
        return 0;
      }
    • 改进后: 使用 dynamic_cast 进行安全的向下转型,如果转换不成功会返回 nullptr

      c++
      #include <iostream>
      using namespace std;
      
      class Base {};
      class Derived : public Base {};
      
      int main() {
        Base* b = new Derived();  // 现在 b 指向 Derived 对象
        Derived* d = dynamic_cast<Derived*>(b); // 安全的向下转型
        if (d) { 
          cout << "Successful downcasting." << endl;
          delete d; 
        } else {
          cout << "Failed downcasting." << endl;
        }
        return 0;
      }

2. 隐式转换 (Implicit Conversion)

  • 改进前: 将一个超出范围的值赋给较小的数据类型, 导致数据丢失

    c++
    #include <iostream>
    using namespace std;
    
    int main() {
        int i = 300;
        char c = i; // 警告:数据丢失,因为 char 只能存储 -128 到 127 的值
        cout << "c = " << c << endl; 
        return 0;
    }
  • 改进后: 在赋值之前进行范围检查, 避免数据丢失

    c++
    #include <iostream>
    using namespace std;
    
    int main() {
        int i = 300;
        if (i >= -128 && i <= 127) {
            char c = i; 
            cout << "c = " << c << endl; 
        } else {
            cout << "Value out of range for char type." << endl;
        }
        return 0;
    }

二、自定义类型转换

  • 改进前: 如果没有定义 Animal 到 Dog 的转换构造函数,则无法进行隐式类型转换

    c++
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class Animal {
    public:
        Animal(const string& name) : name_(name) {}
        virtual void speak() const { cout << "Animal speaking" << endl; }
        string getName() const { return name_; }
    private:
        string name_;
    };
    
    class Dog : public Animal {
    public:
        Dog(const string& name) : Animal(name) {}
        // 没有定义 Animal 到 Dog 的转换构造函数
        void speak() const override { cout << "Woof!" << endl; }
    };
    
    int main() {
        Animal animal{"Buddy"};
        Dog dog = static_cast<Dog>(animal);  // 错误:无法进行隐式类型转换
        dog.speak();
    
        delete animal;
        return 0;
    }
  • 改进后: 定义从 Animal 到 Dog 的转换构造函数,允许隐式类型转换

    c++
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    class Animal {
    public:
        Animal(const string& name) : name_(name) {}
        virtual void speak() const { cout << "Animal speaking" << endl; }
        string getName() const { return name_; }
    private:
        string name_;
    };
    
    class Dog : public Animal {
    public:
        Dog(const string& name) : Animal(name) {}
        Dog(const Animal& a) : Animal(a.getName()) {} // 转换构造函数
        void speak() const override { cout << "Woof!" << endl; }
    };
    
    int main() {
        Animal animal{"Buddy"};
        Dog dog = animal; 
        dog.speak(); // 输出 "Woof!"
    
        return 0;
    }

三、多态 (Polymorphism)

1. 静态多态 (Static Polymorphism)

  • 改进前: 函数重载时,仅返回值类型不同, 编译器无法区分调用哪个函数

    c++
    #include <iostream>
    using namespace std;
    
    int add(int a, int b) {
        return a + b;
    }
    
    double add(int a, int b) { // 错误: 与之前的函数声明冲突
        return a + b;
    }
    
    int main() {
        int result1 = add(1, 2); 
        double result2 = add(1.5, 2.5); 
    
        cout << "result1 = " << result1 << endl;
        cout << "result2 = " << result2 << endl;
        return 0;
    }
  • 改进后: 修改函数名或参数列表,避免函数重载冲突

    c++
    #include <iostream>
    using namespace std;
    
    int addInt(int a, int b) {
        return a + b;
    }
    
    double addDouble(double a, double b) { 
        return a + b;
    }
    
    int main() {
        int result1 = addInt(1, 2); 
        double result2 = addDouble(1.5, 2.5); 
    
        cout << "result1 = " << result1 << endl;
        cout << "result2 = " << result2 << endl;
        return 0;
    }

2. 动态多态 (Dynamic Polymorphism)

  • 改进前: 如果没有将基类函数声明为虚函数,则动态绑定不会发生,调用的是基类的函数

    c++
    #include <iostream>
    
    using namespace std;
    
    class Shape {
    public:
        void draw() const { cout << "Drawing a shape." << endl; } // 非虚函数
    };
    
    class Circle : public Shape {
    public:
        void draw() const { cout << "Drawing a circle." << endl; } 
    };
    
    class Square : public Shape {
    public:
        void draw() const { cout << "Drawing a square." << endl; } 
    };
    
    int main() {
        Shape* shape1 = new Circle();
        Shape* shape2 = new Square();
    
        shape1->draw(); // 调用 Shape::draw() 
        shape2->draw(); // 调用 Shape::draw() 
    
        delete shape1;
        delete shape2;
        return 0;
    }
  • 改进后: 将基类函数声明为虚函数,实现动态绑定

    c++
    #include <iostream>
    
    using namespace std;
    
    class Shape {
    public:
        virtual void draw() const { cout << "Drawing a shape." << endl; } // 虚函数
    };
    
    class Circle : public Shape {
    public:
        void draw() const override { cout << "Drawing a circle." << endl; } 
    };
    
    class Square : public Shape {public:
        void draw() const override { cout << "Drawing a square." << endl; } 
    };
    
    int main() {
        Shape* shape1 = new Circle();
        Shape* shape2 = new Square();
    
        shape1->draw(); // 调用 Circle::draw() 
        shape2->draw(); // 调用 Square::draw() 
    
        delete shape1;
        delete shape2;
        return 0;
    }

作业描述:

请实现一个简单的“动物园管理系统”,该系统中有不同种类的动物(如猫、狗、鸟等)。每种动物都有各自的特点和行为。使用继承和虚函数来实现动物的多态功能。

任务要求:

  1. 创建基类 Animal
    • 包含一个虚函数 void MakeSound() const,表示动物发出的声音。
    • 包含一个虚函数 void ShowInfo() const,显示动物的基本信息(如种类和名字)。
  2. 创建几个派生类,如 Cat(猫), Dog(狗), Bird(鸟):
    • 每个类都继承自 Animal,并实现 MakeSound() 函数,使其输出相应动物的叫声(比如猫叫声为 "Meow",狗叫声为 "Woof",鸟叫声为 "Tweet")。
    • 每个类重载 ShowInfo() 函数,在显示继承来的基本信息的同时显示该类动物的特定信息。
  3. main() 函数中:
    • 创建一个 Animal* 类型的数组,存储不同的动物对象(可以是指针或智能指针)。
    • 遍历这个数组,并调用每个动物的 MakeSound()ShowInfo() 函数,展示多态功能。

代码示例:

cpp
#include <iostream>
#include <string>
#include <vector>

using namespace std;

class Animal {
protected:
    string name;
public:
    Animal(const string& name) : name(name) {}
    virtual void MakeSound() const {};  // 纯虚函数
    virtual void ShowInfo() const {
        cout << "Animal: " << name << endl;
    }
    virtual ~Animal() {}
};

class Cat : public Animal {
public:
    Cat(const string& name) : Animal(name) {}
    void MakeSound() const override {
        cout << "Meow" << endl;
    }
    void ShowInfo() const override {
        Animal::ShowInfo();
        cout << "This is a cat." << endl;
    }
};

class Dog : public Animal {
public:
    Dog(const string& name) : Animal(name) {}
    void MakeSound() const override {
        cout << "Woof" << endl;
    }
    void ShowInfo() const override {
        Animal::ShowInfo();
        cout << "This is a dog." << endl;
    }
};

class Bird : public Animal {
public:
    Bird(const string& name) : Animal(name) {}
    void MakeSound() const override {
        cout << "Tweet" << endl;
    }
    void ShowInfo() const override {
        Animal::ShowInfo();
        cout << "This is a bird." << endl;
    }
};

int main() {
	  Animal* zoo[3];
    Dog dog;
    Cat cat;
    Bird bird;
    zoo[0] = &dog;
    zoo[1] = &cat;
    zoo[2] = &bird;

    for (const auto& animal : zoo) {
        animal->MakeSound();
        animal->ShowInfo();
        cout << "---------------------" << endl;
    }

    // 清理内存
    for (auto& animal : zoo) {
        delete animal;
    }
    
    return 0;
}