C++代码重用:继承与组合的比较

发布时间:2024年01月13日

?

目录

一、简介

继承

组合

二、继承

三、组合

四、案例说明

4.1一个电子商务系统

4.1.1继承方式

在上述代码中,Order类继承自User类。通过继承,Order类获得了User类的成员函数和成员变量,并且可以添加自己的特性。我们重写了displayInfo()函数,以便在Order类中显示订单相关信息。4.1.2组合方式


一、简介

当涉及到代码重用时,继承和组合是两种常见的机制。下面将更详细地介绍它们的特点、使用方式以及各自的优缺点。

  1. 继承

    • 特点:继承是一种创建新类的方式,通过继承已有类的属性和方法来构建新类。在继承关系中,子类(也称为派生类)继承了父类(也称为基类)的成员变量和成员函数,并且可以添加自己的特性。
    • 使用方式:在C++中,可以使用classstruct关键字定义类,在定义派生类时,使用冒号:指定继承关系。在继承中,派生类可以访问父类的公有成员,但不能访问私有成员。
    • 优点:
      • 代码重用:通过继承可以重用基类的代码,减少重复编写相似代码的工作量。
      • 层次结构:继承可以创建一个层次结构,通过将类组织成父子关系,可以更好地组织和管理代码。
    • 缺点:
      • 紧耦合:继承会在派生类和基类之间创建紧密的依赖关系,如果基类发生变化,可能会影响到所有的派生类。
      • 多继承问题:多继承可能引发命名冲突和复杂性增加的问题,需要小心处理。
  2. 组合

    • 特点:组合是一种通过在一个类中包含另一个类的对象来实现代码重用的机制。在组合关系中,一个类(称为组合类)包含另一个类(称为成员类)的对象作为成员变量,通过调用成员对象的方法来实现自己的功能。
    • 使用方式:在C++中,可以在组合类中声明成员对象作为成员变量,并在组合类的方法中调用成员对象的方法来实现功能。
    • 优点:
      • 松耦合:组合关系比继承关系更加松散,类之间的依赖关系相对较弱,修改一个类不会影响到其他类。
      • 灵活性:组合允许动态地改变成员对象,可以在运行时替换成员对象,提供更大的灵活性。
    • 缺点:
      • 冗余代码:组合可能导致一些重复代码,需要在组合类中转发成员对象的方法。
      • 对象管理:组合类需要负责创建和管理成员对象,增加了额外的工作量。

在选择使用继承还是组合时,需要考虑以下因素:

  • 类的关系:如果存在一种“is-a”的关系,即派生类是基类的一种特殊形式,可以选择使用继承。例如,Dog可以被视为Animal的一种特殊类型。
  • 代码重用程度:如果需要重用大量基类代码,可以选择继承。继承允许派生类直接使用基类的功能,减少了代码编写的工作量。
  • 灵活性要求:如果需要更灵活的类关系和低耦合度,可以选择组合。组合允许动态替换成员对象,提供更大的灵活性。

二、继承

继承是一种通过创建一个新类来继承已有类的属性和方法的机制。在继承关系中,子类(派生类)可以继承父类(基类)的成员变量和成员函数,并且可以添加自己的特性。下面是一个简单的示例:

#include <iostream>

class Animal {
public:
    void eat() {
        std::cout << "Animal is eating." << std::endl;
    }
};

class Dog : public Animal {
public:
    void bark() {
        std::cout << "Dog is barking." << std::endl;
    }
};

int main() {
    Dog dog;
    dog.eat();  // 输出 "Animal is eating."
    dog.bark(); // 输出 "Dog is barking."

    return 0;
}

在这个示例中,我们定义了一个基类Animal和一个派生类Dog。派生类Dog继承了基类Animaleat方法,并添加了自己的bark方法。通过创建Dog对象,我们可以调用继承的eat方法和派生类自己的bark方法。

优点:

  1. 代码重用:继承允许派生类重用基类的代码,避免了重复编写相似的代码。
  2. 层次结构:继承可以创建一个层次结构,通过将类组织成父子关系,可以更好地组织和管理代码。

缺点:

  1. 紧耦合:继承会在派生类和基类之间创建紧密的依赖关系,如果基类发生变化,可能会影响到所有的派生类。
  2. 多继承问题:多继承可能会引发命名冲突和复杂性增加的问题。

三、组合

组合是一种通过在一个类中包含另一个类的对象来实现代码重用的机制。在组合关系中,一个类(组合类)包含另一个类(成员类)的对象作为成员变量。下面是一个示例:

#include <iostream>

class Engine {
public:
    void start() {
        std::cout << "Engine is starting." << std::endl;
    }
};

class Car {
private:
    Engine engine;

public:
    void start() {
        engine.start();
        std::cout << "Car is starting." << std::endl;
    }
};

int main() {
    Car car;
    car.start(); // 输出 "Engine is starting." 和 "Car is starting."

    return 0;
}

在这个示例中,我们定义了一个成员类Engine和一个组合类Car。组合类Car包含一个Engine对象作为成员变量,并通过调用Engine对象的方法实现自己的功能。

优点:

  1. 松耦合:组合关系比继承关系更加松散,类之间的依赖关系相对较弱,修改一个类不会影响到其他类。
  2. 灵活性:组合允许动态地改变成员对象,可以在运行时替换成员对象,提供更大的灵活性。

缺点:

  1. 冗余代码:组合可能导致一些重复代码,需要在组合类中转发成员对象的方法。
  2. 对象管理:组合类需要负责创建和管理成员对象,增加了额外的工作量。

结论: 继承和组合都是C++中常用的代码重用机制,它们各有优缺点。在选择使用哪种机制时,需要根据具体的需求和设计要求进行权衡。如果需要创建一个层次结构或者重用大量基类代码,可以选择继承;如果需要更灵活的类关系和低耦合度,可以选择组合。重要的是根据实际情况选择适合的代码重用方式,并结合良好的设计原则来编写高质量的代码。

四、案例说明

4.1一个电子商务系统

其中有两个重要的类:User(用户)和Order(订单)。用户可以下订单,并且一个用户可以有多个订单,因此User类和Order类之间存在一种关系。我们将使用继承和组合两种方式来设计这两个类之间的关系。

4.1.1继承方式

#include <iostream>
#include <string>

// 用户类
class User {
private:
    std::string name;
public:
    User(const std::string& name) : name(name) {}

    void setName(const std::string& newName) {
        name = newName;
    }

    std::string getName() const {
        return name;
    }

    virtual void displayInfo() const {
        std::cout << "User: " << name << std::endl;
    }
};

// 订单类,继承自用户类
class Order : public User {
private:
    std::string orderId;
public:
    Order(const std::string& name, const std::string& orderId)
        : User(name), orderId(orderId) {}

    void setOrderId(const std::string& newOrderId) {
        orderId = newOrderId;
    }

    std::string getOrderId() const {
        return orderId;
    }

    void displayInfo() const override {
        std::cout << "User: " << getName() << ", OrderId: " << orderId << std::endl;
    }
};

int main() {
    User user("John");
    Order order("John", "12345");

    user.displayInfo();
    order.displayInfo();

    return 0;
}

在上述代码中,Order类继承自User类。通过继承,Order类获得了User类的成员函数和成员变量,并且可以添加自己的特性。我们重写了displayInfo()函数,以便在Order类中显示订单相关信息。4.1.2组合方式

#include <iostream>
#include <string>

// 用户类
class User {
private:
    std::string name;
public:
    User(const std::string& name) : name(name) {}

    void setName(const std::string& newName) {
        name = newName;
    }

    std::string getName() const {
        return name;
    }

    void displayInfo() const {
        std::cout << "User: " << name << std::endl;
    }
};

// 订单类,组合了用户类对象
class Order {
private:
    std::string orderId;
    User user;
public:
    Order(const std::string& name, const std::string& orderId)
        : user(name), orderId(orderId) {}

    void setOrderId(const std::string& newOrderId) {
        orderId = newOrderId;
    }

    std::string getOrderId() const {
        return orderId;
    }

    void displayInfo() const {
        std::cout << "User: " << user.getName() << ", OrderId: " << orderId << std::endl;
    }
};

int main() {
    User user("John");
    Order order("John", "12345");

    user.displayInfo();
    order.displayInfo();

    return 0;
}

在这个例子中,Order类包含了一个User类的对象作为成员变量。通过组合,Order类可以调用User类的方法来处理用户相关的操作。

总结: 在这个案例中,我们展示了继承和组合两种不同的代码重用方式。继承适用于存在"是一种"关系的类,并且可以直接使用基类的成员函数和成员变量。组合适用于存在"有一个"关系的类,其中一个类作为另一个类的成员变量,通过调用成员对象的方法来实现功能。选择使用继承还是组合取决于具体的需求和设计目标,需要权衡各自的优缺点来做出决策。

文章来源:https://blog.csdn.net/m0_62338174/article/details/135563679
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。