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

问题描述 投票:0回答:1

假设我有以下界面:

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;
};

虽然这看起来很简单,但有一个问题:像

hasPerson
这样的方法实际上只是
getPerson(name).has_value()
的别名。
ContainerInterface
的所有实现都应该共享这一点,不应该由实现本身来强制执行。事实上,没有什么可以阻止实现执行这样的操作:

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

当然,我可以通过将

hasPerson
作为
ContainerInterface
的一部分来解决这个问题:

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();
}

但这感觉不太令人满意,因为

hasPerson
听起来应该是
ContainerInterface
的一种方法。

有没有更优雅的方法来保持

ContainerInterface
是一个纯接口,同时强制所有实现与
hasPerson
具有相同的含义?

c++ pure-virtual
1个回答
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();
    }
};
© www.soinside.com 2019 - 2024. All rights reserved.