具有便捷方法的 C++ 接口

C++ Interface with convenience methods

提问人:Antonio 提问时间:8/24/2023 最后编辑:Antonio 更新时间:8/24/2023 访问量:84

问:

假设我有以下接口:

struct Person {
    std::string name;
    unsigned short age;
};

class ContainerInterface {
    public:
    virtual ~ContainerInterface () = default;
    virtual void addPerson (Person const&) = 0;
    virtual std::optional<Person> getPerson (std::string const& name) const = 0;
    
    virtual bool hasPerson (std::string const& name) const = 0;
};

此接口可以通过以下方式实现:

class Container: public ContainerInterface {
    public:
    virtual void addPerson (Person const& person) override {
        _people.push_back(person);
    }
    virtual std::optional<Person> getPerson (std::string const& name) const override {
        for (const auto& person: _people) {
            if (person.name == name) return person;
        }
        return std::nullopt;
    }
    virtual bool hasPerson (std::string const& name) const override {
        return getPerson(name).has_value();
    }
    
    private:
    std::vector<Person> _people;
};

虽然这看起来很简单,但有一个问题:像这样的方法实际上只是 的别名。所有实现都应该共享这一点,而不应该留给实现本身来强制执行。事实上,没有什么能阻止实现做这样的事情:hasPersongetPerson(name).has_value()ContainerInterface

class BrokenContainer: public Container {
    public:
    virtual bool hasPerson (std::string const& name) const override {
        return false;
    }
};

当然,我可以通过作为以下部分的一部分来实现此问题:hasPersonContainerInterface

class ContainerInterface {
    public:
    virtual ~ContainerInterface () = default;
    virtual void addPerson (Person const&) = 0;
    virtual std::optional<Person> getPerson (std::string const& name) const = 0;
    
    virtual bool hasPerson (std::string const& name) const final;
};

// Should always do this, regardless of implementation
bool ContainerInterface::hasPerson (std::string const& name) const {
    return getPerson(name).has_value();
}

但是我的界面不再是一个纯粹的界面了。在某些生产设置中,我可以将宏粘附在我的接口上,将其标记为纯接口,并检查它是否未实现任何方法。如果我使用这种方法,我将无法将我的类标记为纯接口。ContainerInterface

另一种解决方法是实现为免费函数:hasPerson

bool hasPerson (ContainterInterface const& container, std::string const& name) {
  return container.getPerson(name).has_value();
}

但这感觉并不令人满意,因为听起来它应该是一种 .hasPersonContainerInterface

有没有更优雅的方法来保持纯接口,同时强制所有实现都具有相同的含义?ContainerInterfacehasPerson

C++ 纯虚拟

评论

4赞 Yksisarvinen 8/24/2023
C++没有“接口”的概念。在同一个类中定义一些方法和纯方法是完全可以接受的。
2赞 Ted Lyngmo 8/24/2023
“但是我的 ContainerInterface 不再是一个纯粹的接口了”——甚至 java 也放弃了并允许在接口类中使用默认实现。
1赞 Caleth 8/24/2023
“如果我使用这种方法,我将无法将我的类标记为纯接口。所以?

答:

1赞 Ted Lyngmo 8/24/2023 #1

解决方法是不让其他类直接继承。ContainerInterface

class ContainerInterface {                               // pure
public:
    virtual ~ContainerInterface() = default;

    virtual void addPerson (Person const&) = 0;
    virtual std::optional<Person> getPerson (std::string const& name) const = 0;
    
    virtual bool hasPerson (std::string const& name) const = 0;

private:
    ContainerInterface() = default;                      // private
    friend class ContainerBase;                          // except for ContainerBase
};

class ContainerBase : public ContainerInterface {
public:
    bool hasPerson (std::string const& name) const override final {
//                                                          ^^^^^
        return getPerson(name).has_value();
    }
};

class Container : public ContainerBase {
//                       ^^^^^^^^^^^^^
    //...
};

这与为接口提供无法重写的默认实现基本相同:

class ContainerInterface {
public:
    virtual ~ContainerInterface() = default;
    virtual void addPerson (Person const&) = 0;
    virtual std::optional<Person> getPerson (std::string const& name) const = 0;
    
    virtual bool hasPerson (std::string const& name) const final {
        return getPerson(name).has_value();
    }
};