引用语义多态 vs 值语义多态

值语义多态(Value Semantic Polymorphism)和引用语义多态(Reference Semantic Polymorphism)是C++中多态性的两种不同实现方式,它们在管理对象生命周期和接口设计中有着本质的差异。

特性 / 多态类型 值语义多态 (Value Semantic Polymorphism) 引用语义多态 (Reference Semantic Polymorphism)
定义 通过拷贝实现多态性,创建对象的副本 通过引用或指针实现多态性,不创建对象副本
实现方式 使用模板和函数重载 使用虚函数和继承
优点 简单直接,内存管理简单 高效处理大对象,避免不必要的拷贝
缺点 大对象处理时性能低下(频繁拷贝) 需要复杂的内存管理,代码可能难以维护
应用场景 小对象,或对性能要求不高的场景 大型对象,性能要求高的场景

值语义多态 (Value Semantic Polymorphism)

#include <iostream>
#include <vector>

class Shape {
public:
    virtual void draw() const = 0;
    virtual ~Shape() {}
    // 注意:为实现值语义,提供一个克隆方法
    virtual Shape* clone() const = 0;
};

class Circle : public Shape {
public:
    void draw() const override {
        std::cout << "Drawing Circle\n";
    }
    Circle* clone() const override {
        return new Circle(*this);
    }
};

class Square : public Shape {
public:
    void draw() const override {
        std::cout << "Drawing Square\n";
    }
    Square* clone() const override {
        return new Square(*this);
    }
};

int main() {
    std::vector<Shape*> shapes;
    shapes.push_back(new Circle());
    shapes.push_back(new Square());

    std::vector<Shape*> shapes_copy;
    for (auto* shape : shapes) {
        shapes_copy.push_back(shape->clone()); // 使用克隆方法
    }

    for (auto* shape : shapes_copy) {
        shape->draw(); // 使用拷贝后的对象
    }

    // 清理
    for (auto* shape : shapes) {
        delete shape;
    }
    for (auto* shape : shapes_copy) {
        delete shape;
    }
}

这个例子中,Shape 类定义了一个 clone 方法,允许子类如 Circle 和 Square 创建自己的副本。这是值语义多态的一个例子,其中每个形状的副本都是独立的,并且可以独立操作。

或者使用 visit 与 variant 组合生成委托函数表以实现值语义的运行时多态

#include <iostream>
#include <variant>
#include <vector>

// 定义几种不同的形状
struct Circle {
    void draw() const { std::cout << "Drawing a circle.\n"; }
};

struct Square {
    void draw() const { std::cout << "Drawing a square.\n"; }
};

struct Triangle {
    void draw() const { std::cout << "Drawing a triangle.\n"; }
};

// 创建一个类型别名,代表可以是Circle, Square或Triangle的变体
using Shape = std::variant<Circle, Square, Triangle>;

int main() {
    // 创建一个Shape的vector
    std::vector<Shape> shapes;
    shapes.push_back(Circle());
    shapes.push_back(Square());
    shapes.push_back(Triangle());

    // 遍历shapes,使用visit来调用正确的draw函数
    for (const auto& shape : shapes) {
        std::visit([](const auto& s) { s.draw(); }, shape);
    }

    return 0;
}

在这个例子中,std::variant<Circle, Square, Triangle> 类型的 Shape 变量可以存储 Circle、Square 或 Triangle 中的任何一个。std::visit 函数用于访问 variant 并调用正确的 draw 方法。这个方法的优势在于类型安全和值语义的使用,它允许在不使用继承和虚函数的情况下实现多态。这种方法特别适用于类型数量有限且确定的情况

引用语义多态 (Reference Semantic Polymorphism)

#include <iostream>
#include <vector>
#include <memory>

class Shape {
public:
    virtual void draw() const = 0;
    virtual ~Shape() {}
};

class Circle : public Shape {
public:
    void draw() const override {
        std::cout << "Drawing Circle\n";
    }
};

class Square : public Shape {
public:
    void draw() const override {
        std::cout << "Drawing Square\n";
    }
};

int main() {
    std::vector<std::shared_ptr<Shape>> shapes;
    shapes.push_back(std::make_shared<Circle>());
    shapes.push_back(std::make_shared<Square>());

    for (const auto& shape : shapes) {
        shape->draw(); // 使用原始对象
    }
}

在这个例子中,我们使用智能指针(std::shared_ptr)来管理 Shape 对象。这是引用语义多态的一个例子,其中形状通过它们的引用(在这种情况下是智能指针)传递,无需创建对象副本。通过多态,我们可以在 Shape 类型的容器中存储不同类型的形状,并在运行时调用适当的 draw 方法。

这两个例子展示了值语义和引用语义在C++中的实现和使用方式。值语义重在创建独立的副本,适用于小对象或对性能要求不高的场景,而引用语义更适合处理大型对象和性能敏感的场景。

打赏作者