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;
}