【C++】接口和类的详解

目录

一、类(Class)

1.1. 类定义

1.2. 成员变量

1.3. 成员函数

1.4. 访问修饰符

1.5. 构造函数和析构函数

1.6. 实例化

1.7. 具体实现

1.8. 继承

1.9. 示例

二、接口(通过抽象类和纯虚函数模拟)

2.1. 接口的概念

2.2. 抽象类(模拟接口)

2.3. 纯虚函数

2.4. 具体实现

2.5. 继承

2.6. 示例

三、主要区别梳理

3.1. C++类的特性

3.2. 模拟接口的抽象类的特性

3.3. C++类和接口的区别

四、注意事项

4.1. 清晰地区分类和接口

4.2. 合理使用继承

4.3. 虚析构函数

4.4. 避免接口污染

4.5. 遵循接口隔离原则

4.6. 注意接口和类的关系

4.7. 考虑使用组合而不是继承

4.8. 文档和命名


在C++中,接口(Interface)和类(Class)是面向对象编程(OOP)的两个核心概念,但它们的具体实现和用途有所不同。不过,需要注意的是,C++标准库本身并没有直接提供像Java或C#那样的“接口”关键字。但是,C++可以通过抽象类(Abstract Class)和纯虚函数(Pure Virtual Function)来模拟接口的概念。

一、类(Class)

C++ 类允许定义具有属性(也称为成员变量或数据成员)和方法(也称为成员函数或成员方法)的数据类型。类是对现实世界中实体或概念的抽象表示,通过对象来实例化这些类。

1.1. 类定义

  • 在C++中,类通过关键字 class 来定义。类定义包括类名、类体(由花括号包围)以及类体中定义的成员变量(属性)和成员函数(方法)。

1.2. 成员变量

  • 成员变量是类的属性,用于存储对象的状态信息。

1.3. 成员函数

  • 成员函数是类的方法,定义了对象可以执行的操作。成员函数可以访问类的成员变量,也可以被其他成员函数调用。

1.4. 访问修饰符

C++ 类中的成员(变量和函数)可以有不同的访问级别,这些级别通过访问修饰符来指定:

  • public:成员在类的外部也可以被访问。
  • protected:成员在类的外部不可直接访问,但可以被派生类(子类)访问。
  • private:成员在类的外部和派生类中均不可直接访问,只能在类的内部(包括成员函数中)访问。

1.5. 构造函数和析构函数

  • 构造函数:特殊类型的成员函数,用于在创建对象时初始化对象。它的名称与类名相同,且没有返回类型(包括 void)。
  • 析构函数:也是一个特殊类型的成员函数,用于在对象生命周期结束时执行清理工作。析构函数的名称是在类名前加上波浪号(~),同样没有返回类型。

1.6. 实例化

  • 类的实例(对象)可以被创建并用于执行类的成员函数。

1.7. 具体实现

  • 类可以包含具体的实现代码(即,成员函数可以有定义体)。

1.8. 继承

  • 一个类可以继承另一个类的属性和方法,支持单继承和多继承(通过虚继承解决钻石问题)。

1.9. 示例

下面是一个简单的C++类示例,表示一个具有姓名和年龄属性的人:

#include <iostream>  
#include <string>  
  
class Person {  
private:  
    std::string name;  
    int age;  
  
public:  
    // 构造函数  
    Person(std::string n, int a) : name(n), age(a) {}  
  
    // 成员函数:显示人的信息  
    void display() const {  
        std::cout << "Name: " << name << ", Age: " << age << std::endl;  
    }  
  
    // 成员函数:设置年龄  
    void setAge(int a) {  
        age = a;  
    }  
  
    // 成员函数:获取年龄  
    int getAge() const {  
        return age;  
    }  
  
    // 成员函数:设置姓名(演示友元函数之前的必要性,虽然此例中未使用)  
    // ...  
};  
  
int main() {  
    Person person1("Alice", 30);  
    person1.display(); // 显示:Name: Alice, Age: 30  
  
    person1.setAge(31);  
    person1.display(); // 显示:Name: Alice, Age: 31  
  
    return 0;  
}

在这个例子中,Person 类有两个私有成员变量 name 和 age,以及几个公共成员函数,包括构造函数、displaysetAge 和 getAge。通过公共成员函数,我们可以访问和修改对象的私有成员变量,这符合封装的原则。

二、接口(通过抽象类和纯虚函数模拟)

在C++中,接口的概念并不像在一些其他面向对象编程语言(如Java或C#)中那样直接支持。然而,C++通过抽象类(包含至少一个纯虚函数的类)来模拟接口的行为。纯虚函数是没有函数体的虚函数,它强制派生类(子类)实现该函数。

2.1. 接口的概念

接口定义了一组方法,但不实现它们。它指定了派生类必须遵循的契约。在C++中,这通过创建一个包含纯虚函数的抽象类来实现。

2.2. 抽象类(模拟接口)

  • 抽象类是不能被实例化的类。
  • 它至少包含一个纯虚函数。
  • 抽象类通常用作基类,派生类继承它并提供纯虚函数的实现。

2.3. 纯虚函数

  • 纯虚函数是在基类中声明的,但没有在基类中定义的虚函数。它使用 = 0 语法来标记。

2.4. 具体实现

  • 接口本身不包含任何具体实现(即,所有成员函数都是纯虚的,没有定义体)。

2.5. 继承

一个类可以继承一个或多个接口(抽象基类),必须为所有继承的纯虚函数提供具体实现,除非该类也被声明为抽象类。

2.6. 示例

以下是一个C++接口(通过抽象类模拟)的示例:

#include <iostream>  
  
// 模拟接口的抽象类  
class Shape {  
public:  
    // 纯虚函数,强制派生类实现  
    virtual void draw() const = 0;  
  
    // 虚析构函数,确保通过基类指针删除派生类对象时调用正确的析构函数  
    virtual ~Shape() {}  
  
    // 注意:接口通常不包含数据成员和非纯虚函数,但这不是强制的  
};  
  
// 派生类,实现Shape接口  
class Circle : public Shape {  
public:  
    // 实现纯虚函数  
    void draw() const override {  
        std::cout << "Drawing Circle" << std::endl;  
    }  
};  
  
// 另一个派生类,也实现Shape接口  
class Rectangle : public Shape {  
public:  
    // 实现纯虚函数  
    void draw() const override {  
        std::cout << "Drawing Rectangle" << std::endl;  
    }  
};  
  
// 使用接口(抽象类)的示例  
int main() {  
    Shape* shape1 = new Circle();  
    shape1->draw(); // 输出:Drawing Circle  
    delete shape1;  
  
    Shape* shape2 = new Rectangle();  
    shape2->draw(); // 输出:Drawing Rectangle  
    delete shape2;  
  
    return 0;  
}

三、主要区别梳理

C++中的类和接口之间的区别主要在于它们的特性和用途。

3.1. C++类的特性

  • 实例化:类可以被实例化,即可以创建该类的对象。
  • 成员:类可以包含数据成员(属性)和成员函数(方法),这些成员定义了对象的状态和行为。
  • 访问修饰符:类中的成员可以有不同的访问级别(public、protected、private)。
  • 构造函数和析构函数:类可以有构造函数来初始化对象,以及析构函数来清理资源。
  • 继承:类可以继承自其他类,包括抽象类(模拟的接口)。

3.2. 模拟接口的抽象类的特性

  • 不能实例化:由于至少包含一个纯虚函数,抽象类不能被实例化。
  • 纯虚函数:抽象类至少包含一个纯虚函数,该函数在基类中没有实现,派生类必须提供实现。
  • 强制实现:通过纯虚函数,抽象类强制派生类实现特定的行为或方法。
  • 继承:抽象类通常被用作基类,派生类继承它并提供纯虚函数的实现。
  • 接口契约:抽象类模拟的接口定义了派生类必须遵循的契约或规范。

3.3. C++类和接口的区别

  • 实例化:类可以被实例化,而接口(在C++中通过抽象类模拟)不能被实例化。

  • 实现:类可以包含具体实现,而接口则不包含任何实现(所有成员函数都是纯虚的)。

  • 目的:类用于定义对象的模板,包含数据和行为。接口则主要用于定义一组规范或契约,这些规范由派生类实现。接口更关注“做什么”,而不是“怎么做”。

  • 成员:类可以包含数据成员和成员函数(包括非虚函数、虚函数和纯虚函数)。接口(抽象类模拟的)通常只包含纯虚函数和静态成员(如果有的话),不包含数据成员(尽管这不是强制的,但通常不推荐)。

  • 灵活性:类提供了更多的灵活性,因为它们可以包含数据和行为。接口则更加严格,它强制派生类遵循特定的行为模式。

  • 用途:类通常用于实现具体的功能或数据结构。接口则用于定义一组方法,这些方法在派生类中实现,以实现多态性和解耦。

  • 设计原则:接口遵循单一职责原则和开闭原则,即一个接口应该只负责一组相关的功能,且接口应该是可扩展的,但不应该修改。类则可以根据需要包含多个功能,并且可以根据需要进行修改。

  • 继承:类可以从另一个类继承实现和接口,而接口(抽象基类)主要用于定义接口,其继承主要是为了强制派生类实现特定方法。

总的来说,C++中的类和接口(通过抽象类模拟)在面向对象编程中扮演着不同的角色,它们共同支持代码的可重用性、可维护性和可扩展性。​​​​​​

四、注意事项

在C++中使用类和模拟的接口(通过抽象类)时,有几个注意事项需要考虑,以确保代码的健壮性、可维护性和可扩展性。

4.1. 清晰地区分类和接口

  • :通常包含数据成员(属性)和成员函数(方法),用于表示具有特定行为的数据结构。
  • 接口(模拟):仅包含纯虚函数的抽象类,用于定义一组必须由派生类实现的方法。接口不应该包含数据成员(尽管技术上可以,但通常不推荐),也不应该包含实现体(除了析构函数可能是虚析构函数外)。

4.2. 合理使用继承

  • 当你希望一个类继承另一个类的行为时,使用继承。但是,要谨慎使用继承,因为它会增加类之间的耦合度。
  • 使用接口(抽象类)来定义一组必须被实现的方法,这样可以在保持低耦合度的同时实现多态性。

4.3. 虚析构函数

  • 如果你的类有虚函数(包括纯虚函数),并且你打算通过基类指针来删除派生类对象,那么你应该在基类中定义一个虚析构函数。这可以确保在删除对象时调用正确的析构函数,从而避免资源泄漏。

4.4. 避免接口污染

  • 接口应该只包含必需的方法,避免在接口中添加不必要的函数,这会导致接口污染,使得接口变得庞大且难以维护。
  • 如果发现接口中的方法不再需要或不应该由所有派生类实现,考虑将其移动到另一个接口中或完全移除。

4.5. 遵循接口隔离原则

  • 接口隔离原则(Interface Segregation Principle)建议将臃肿的接口拆分成更小的和更具体的接口。这样,客户端只需要知道它们感兴趣的方法即可。
  • 遵循这一原则可以减少客户端对接口的依赖,提高系统的灵活性和可维护性。

4.6. 注意接口和类的关系

  • 接口定义了派生类必须遵循的契约,但它本身并不提供实现。类则提供了实现,并且可能包含额外的数据和行为。
  • 在设计系统时,要清楚地理解哪些部分是稳定的、不会频繁变化的(这些部分适合放在接口中),哪些部分是可能会变化的(这些部分适合放在具体的类中)。

4.7. 考虑使用组合而不是继承

  • 有时候,通过组合(即在一个类中包含另一个类的对象)来实现功能比使用继承更为合适。组合可以减少类之间的耦合度,并且更加灵活。
  • 在决定是否使用继承时,要仔细考虑你的需求以及继承带来的好处和坏处。

4.8. 文档和命名

  • 对你的类和接口进行良好的文档编写,包括它们的用途、成员变量和成员函数的描述等。
  • 使用清晰、有意义的命名来命名你的类和接口,以便其他开发者能够理解它们的用途和功能。

遵循这些注意事项可以帮助你更好地在C++中使用类和模拟的接口,编写出更加健壮、可维护和可扩展的代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

byte轻骑兵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值