1、接口与抽象类
C++ 接口是使用抽象类来实现的,抽象类与数据抽象互不混淆,数据抽象是一个把实现细节与相关的数据分离开的概念。
如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。纯虚函数是通过在声明中使用 "= 0"
来指定的。在 C++ 语言中没有抽象类的概念,可通过纯虚函数来实现抽象类。纯虚函数是指只定义原型的成员函数,如果一个 C++ 类中存在纯虚函数便就成为了抽象类。
例如,
#include <iostream> using namespace std; class Shape { public: virtual double area() = 0; }; class Rect : public Shape { int ma; int mb; public: Rect(int a, int b) { ma = a; mb = b; } double area() { return ma * mb; } }; class Circle : public Shape { int mr; public: Circle(int r) { mr = r; } double area() { return 3.14 * mr * mr; } }; void area(Shape* p) { double r = p->area(); cout << "r = " << r << endl; } int main() { Rect rect(1, 5); Circle circle(10); area(&rect); area(&circle); return 0; }
注意:
1) “= 0”
是用于告诉编译器当前是声明纯虚函数,因此不需要定义函数体。
2)抽象类用于描述现实世界中的抽象概念,抽象类只能被继承而不能创建对象。
3)在 C++ 中没有抽象类的概念,是通过纯虚函数来实现抽象类。
4)类中只存在纯虚函数的时候才能成为接口。
5)接口是一种特殊的抽象类。
2、抽象类与普通类的区别
普通类(Concrete Class)是一个可以直接实例化对象的类。它可以包含完全实现的方法和数据成员。抽象类(Abstract Class)是一个包含至少一个纯虚函数的类,不能直接实例化对象。抽象类通常用于定义接口或为派生类提供基类。普通类可以包含实现的函数,也可以有虚函数(但不要求是纯虚函数)。普通类中的函数都有具体的实现。抽象类:必须包含至少一个纯虚函数(pure virtual function),这是一个没有函数体的函数,必须由派生类提供实现。普通类可以直接实例化对象。抽象类不能直接实例化对象。如果你尝试创建抽象类的对象,编译器会报错。派生类必须实现抽象类中的所有纯虚函数,否则派生类也将变成抽象类,不能直接实例化。
1)普通类(Concrete Class)
#include <iostream> using namespace std; // 普通类:表示矩形 class Rectangle { public: void draw() { cout << "Drawing Rectangle" << endl; } }; int main() { Rectangle rect; rect.draw(); // 输出:Drawing Rectangle return 0; }
2)抽象类(Abstract Class)
#include <iostream> using namespace std; // 抽象类:定义绘图接口 class Shape { public: // 纯虚函数(接口方法) virtual void draw() = 0; // 纯虚函数,派生类必须实现 virtual ~Shape() {} // 虚析构函数 }; // 派生类:实现具体的绘制方法 class Circle : public Shape { public: void draw() override { cout << "Drawing Circle" << endl; } }; class Rectangle : public Shape { public: void draw() override { cout << "Drawing Rectangle" << endl; } }; int main() { // Shape shape; // 错误:不能实例化抽象类 Shape* shape1 = new Circle(); // 使用基类指针 shape1->draw(); // 输出:Drawing Circle Shape* shape2 = new Rectangle(); // 使用基类指针 shape2->draw(); // 输出:Drawing Rectangle delete shape1; delete shape2; return 0; }
3、抽象类的应用场景
抽象类主要用于定义接口和提供共享的行为模板。它们的应用场景广泛,尤其在需要多态和类之间的统一接口时非常有用。
1)定义接口
抽象类常用来定义统一的接口,确保所有派生类都实现某些特定的行为。接口定义了一组必须实现的方法,而不关心具体的实现方式。
#include <iostream> #include <vector> using namespace std; // 抽象类:定义绘图接口 class Shape { public: virtual void draw() = 0; // 纯虚函数,派生类必须实现 virtual ~Shape() {} // 虚析构函数 }; // 派生类:Circle class Circle : public Shape { public: void draw() override { cout << "Drawing Circle" << endl; } }; // 派生类:Rectangle class Rectangle : public Shape { public: void draw() override { cout << "Drawing Rectangle" << endl; } }; // 使用示例 int main() { // 创建一个 Shape 类型的指针,指向不同的具体形状 Shape* shape1 = new Circle(); Shape* shape2 = new Rectangle(); // 使用多态调用 draw() 方法 shape1->draw(); // 输出:Drawing Circle shape2->draw(); // 输出:Drawing Rectangle // 使用 vector 管理多个形状对象 vector<Shape*> shapes; shapes.push_back(new Circle()); shapes.push_back(new Rectangle()); // 遍历并绘制所有形状 for (Shape* shape : shapes) { // 根据实际类型调用对应的 draw() 方法 shape->draw(); } // 清理动态分配的内存 for (Shape* shape : shapes) { delete shape; } return 0; }
2)多态
抽象类允许通过基类指针或引用操作不同类型的派生类对象,从而实现多态。通过多态,代码可以基于对象的类型而在运行时决定具体的操作。
#include <iostream> #include <vector> using namespace std; // 抽象基类 class Character { public: virtual void update() = 0; // 纯虚函数,派生类必须实现 virtual ~Character() {} // 虚析构函数 }; // 派生类:敌人 class Enemy : public Character { public: void update() override { cout << "Updating Enemy" << endl; } }; // 派生类:玩家 class Player : public Character { public: void update() override { cout << "Updating Player" << endl; } }; // 程序主函数 int main() { // 创建一个敌人对象和一个玩家对象 Enemy enemy; Player player; // 将派生类对象通过基类指针来操作 Character* character1 = &enemy; // 基类指针指向派生类对象 Character* character2 = &player; // 使用多态调用派生类的 update() 方法 character1->update(); // 输出:Updating Enemy character2->update(); // 输出:Updating Player // 使用容器存储基类指针,实现统一操作 vector<Character*> characters = { &enemy, &player }; for (Character* character : characters) { // 通过基类指针调用相应的 update() 方法 character->update(); } return 0; }
3)设计模板类
在一些情况下,抽象类用于设计模板类,使得派生类可以填充细节,从而避免重复代码的编写。
#include <iostream> #include <string> using namespace std; // 抽象基类:Database class Database { public: virtual void connect() = 0; // 纯虚函数,派生类必须实现 virtual void query(const string& sql) = 0; // 纯虚函数 virtual void disconnect() = 0; // 纯虚函数 virtual ~Database() {} }; // MySQLDatabase 类,派生自 Database class MySQLDatabase : public Database { public: void connect() override { cout << "Connecting to MySQL Database" << endl; } void query(const string& sql) override { cout << "Querying MySQL Database: " << sql << endl; } void disconnect() override { cout << "Disconnecting from MySQL Database" << endl; } }; // SQLiteDatabase 类,派生自 Database class SQLiteDatabase : public Database { public: void connect() override { cout << "Connecting to SQLite Database" << endl; } void query(const string& sql) override { cout << "Querying SQLite Database: " << sql << endl; } void disconnect() override { cout << "Disconnecting from SQLite Database" << endl; } }; // 使用示例 int main() { // 创建一个 MySQL 数据库对象 Database* db1 = new MySQLDatabase(); db1->connect(); db1->query("SELECT * FROM users;"); db1->disconnect(); delete db1; // 释放内存 // 创建一个 SQLite 数据库对象 Database* db2 = new SQLiteDatabase(); db2->connect(); db2->query("SELECT * FROM products;"); db2->disconnect(); delete db2; // 释放内存 return 0; }