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
进行安全的向下转型,如果转换不成功会返回 nullptrc++#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; }
作业描述:
请实现一个简单的“动物园管理系统”,该系统中有不同种类的动物(如猫、狗、鸟等)。每种动物都有各自的特点和行为。使用继承和虚函数来实现动物的多态功能。
任务要求:
- 创建基类
Animal
:- 包含一个虚函数
void MakeSound() const
,表示动物发出的声音。 - 包含一个虚函数
void ShowInfo() const
,显示动物的基本信息(如种类和名字)。
- 包含一个虚函数
- 创建几个派生类,如
Cat
(猫),Dog
(狗),Bird
(鸟):- 每个类都继承自
Animal
,并实现MakeSound()
函数,使其输出相应动物的叫声(比如猫叫声为 "Meow",狗叫声为 "Woof",鸟叫声为 "Tweet")。 - 每个类重载
ShowInfo()
函数,在显示继承来的基本信息的同时显示该类动物的特定信息。
- 每个类都继承自
- 在
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;
}