C++ 中,接口通常是通过抽象类来实现的。抽象类是一个包含至少一个纯虚函数的类,不能直接实例化对象。纯虚函数是没有定义的函数,强制派生类实现该函数。接口可以包含多种成员函数,如普通函数、纯虚函数和虚析构函数等。抽象类是C++面向对象编程中非常重要的概念,它为我们提供了一种定义接口、实现多态和强制实现的方法。通过抽象类,可以更好地组织代码,提高代码的可扩展性和可维护性。本文主要介绍C++ 接口(抽象类)。

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

推荐文档