我们是否有更好的方法来返回C++中的抽象类

Do we have a better way of returning abstract classes in C++?

本文关键字:返回 C++ 抽象类 方法 是否 更好 我们      更新时间:2023-10-16

我想开始在我的C++代码中加入一些接口,以便我更容易使用模拟进行单元测试。

这样做的问题是从C++的方法返回抽象类是一种痛苦。不能按值返回,因此需要返回指针或引用。

考虑到过去六七年来C++的所有发展,我想我会问我们是否有更好的方法来返回抽象基类。没有噪音的界面看起来像这样,但我相信这是不可能的。

IBaseInterface getThing() {return DerivedThing{};}

我记得过去这样做的方式是使用指针(现在可能是智能指针):

std::unique_ptr<IBaseInterface> getThing() {return std::make_unique<DerivedThing>();}
指针

的问题在于我从未真正打算利用 nullptr,因此处理指针而不是值的开销和噪音使我作为读者没有任何价值。

有没有我不知道的更好的方法来处理这个问题?

编辑:提供完整的示例,包括使多态句柄可复制。

#include <iostream>
#include <utility>
#include <memory>
struct IBaseInterface {
    IBaseInterface() = default;
    IBaseInterface(IBaseInterface const&) = default;
    IBaseInterface(IBaseInterface &&) = default;
    IBaseInterface& operator=(IBaseInterface const&) = default;
    IBaseInterface& operator=(IBaseInterface &&) = default;
    virtual ~IBaseInterface() = default;
    virtual std::unique_ptr<IBaseInterface> clone() const = 0;
    virtual void do_thing() = 0;
};
struct handle
{
    handle(std::unique_ptr<IBaseInterface> ptr)
    : _impl(std::move(ptr))
    {}
    handle(handle const& r)
    : _impl(r._impl->clone())
    {}
    handle(handle && r)
    : _impl(std::move(r._impl))
    {}
    handle& operator=(handle const& r)
    {
        auto tmp = r;
        std::swap(_impl, tmp._impl);
        return *this;
    }
    handle& operator=(handle && r)
    {
        _impl = std::move(r._impl);
        return *this;
    }

    // interface here
    void do_thing() { _impl->do_thing(); }
private:
    std::unique_ptr<IBaseInterface> _impl;
};
struct DerivedThing : IBaseInterface
{
    std::unique_ptr<IBaseInterface> clone() const override
    {
        return std::make_unique<DerivedThing>(*this);
    }
    void do_thing() override
    {
        std::cout << "I'm doing something" << std::endl;
    }
};
handle make_thing()
{
    return handle(std::make_unique<DerivedThing>());
};
int main()
{
    auto a = make_thing();
    auto b = a;
    a.do_thing();
    b.do_thing();
    return 0;
}

现在使用句柄,就好像它有(可移动的)值语义一样