重构从std::容器继承的类

Refactoring a class which inherited from a std::container

本文关键字:继承 std 重构      更新时间:2023-10-16

我有一个中等大小的代码块,包含5个类,它们都是从标准容器继承的。例如:

class Step : public std::vector<unsigned int>
{
public:
    friend std::ostream& operator<<(std::ostream& outStream, const Step& step);
    Step& operator =(const Step& rhv);
    static Step fromString(const std::string &input);
    std::string name;
};

我知道从标准容器继承是个坏主意,所以我将通过添加父数据类型的子对象来删除所有继承:

class Step
{
public:
    friend std::ostream& operator<<(std::ostream& outStream, const Step& step);
    Step& operator =(const Step& rhv);
    static Step fromString(const std::string &input);
    std::string name;
    // std::vector interface:
    inline std::vector<unsigned int>::const_iterator begin() const {return data.begin();}
    inline std::vector<unsigned int>::const_iterator end() const {return data.end();}
    inline size_t size() const {return data.size();}
    typedef std::vector<unsigned int>::const_iterator const_iterator;
private:
    std::vector<unsigned int> data;
};

我这样做是因为代码在其他程序中被广泛使用,并且更改结构的成本非常高。

问题是:你建议如何通过小的修改来修改代码?(尽可能少)

澄清:我有一些从stl容器继承的类。有很多代码在使用它们。我的问题是,如何在不更改使用这些类的代码的情况下删除这种邪恶的继承?

我知道从标准容器继承是个坏主意

这在很多情况下都是有道理的。如果你认为总是是个坏主意,那么你错了。如果从标准容器继承使代码变得简短且易于阅读,那么从STL容器继承是个好主意。在MY看来,您的第一个类(继承std::vector)比第二个好。

问题是:你建议如何通过小的修改来修改代码?

如果你只想浪费时间…

首先,你可以删除"公共朋友"功能,这没有任何意义。

之后声明几个typedef。

typedef std::string Name;
class Step{
protected:
    typedef std::vector<int> Data;
    Data data;
public:
    typedef Data::const_iterator ConstIterator;
    Step& operator=(const Step& other);
    static Step fromString(const std::string &input);
    Name name;
    ConstIterator begin() const;
    ConstIterator end() const;
    size_t size() const;
};

并在所有与Step交互的代码中使用这些typedeffed类型。这样,您以后就可以更改内部类型(例如,用std::deque替换std::vector,或者实现自定义迭代器类),而无需修改整个项目。

一个潜在的解决方案:使继承私有(或受保护,如果有意义的话),并将using作为相关成员:

class Step : private std::vector<int>
{
    typedef std::vector<int> base_;
public:
    using base_::operator[];
    using base_::size;
    ...
};

如果继承确实简化了代码,那么您可能会喜欢这个解决方案。从容器继承的真正问题是,一旦向Step类添加成员,向vector的转换可能会给您带来麻烦:析构函数会更改,而且您知道,它一开始就不是虚拟的。私有继承解决了这个问题。

最好的解决方案是首先将您的功能重构为免费功能:

namespace Step2 {
  std::vector<unsigned int> fromString(const std::string &input);
}

然后为旧的API提供一个独立的头:

  DEPRECATED class Step : public std::vector<unsigned int>
  {
    public:
    inline static Step fromString(std::string const& input);
    {
      return Step(Step2::fromString(input));
    }
  };
typedef std::vector<unsigned int> data_t;
data_t data;

引入typedef简化了方法的所有签名,并降低了向量类型更改的风险