问题
I've a medium chunk of code containing 5 classes that they're all inherited from standard containers. For example:
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;
};
I know it's a bad idea to inherit from standard containers, so I'm going to remove all inheritances by adding a subobject of parent datatype:
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;
};
I'm doing this because the code is widely used in other programs and changing structure is very costly.
And question: what do you suggest to revise the code by small changes? (as less as possible)
Clarification: I have a some classes inherited from stl containers. And there are lots of codes using them. My question is that how do I remove that evil inheritance without changing codes using those classes?
回答1:
One potential solution: making the inheritance private (or protected, if it makes sense), and using the relevant members:
class Step : private std::vector<int>
{
typedef std::vector<int> base_;
public:
using base_::operator[];
using base_::size;
...
};
If inheritance indeed made the code simpler, then you may like this solution. The real problem with inheriting from containers is that the conversion to vector may get you in trouble once you add members to the Step class: the destructor changes, and you know, it's not virtual in the first place. Private inheritance solves this problem.
回答2:
I know it's a bad idea to inherit from standard containers
It makes sense in many scenarios. If you think that it is always a bad idea, then you're mistaken. If inheriting from standard container makes your code shorted and easier to read, then inheriting from STL container is a good idea. In MY opinion your first class (that inherits std::vector) is better than second one.
And question: what do you suggest to revise the code by small changes?
If you want to waste your time just for the heck of it...
For a start you could remove "public friend" function which doesn't make any sense.
After that declare several typedefs.
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;
};
And use those typedeffed types in all code that interacts with Step. This way you will be able to change internal types later (for example, replace std::vector with std::deque, or implement custom iterator class) without having to modify entire project.
回答3:
The best solution would be to first refactor your functionality as free functions:
namespace Step2 {
std::vector<unsigned int> fromString(const std::string &input);
}
and then provide a standalone header for the old API:
DEPRECATED class Step : public std::vector<unsigned int>
{
public:
inline static Step fromString(std::string const& input);
{
return Step(Step2::fromString(input));
}
};
回答4:
typedef std::vector<unsigned int> data_t;
data_t data;
Introducing typedef simplifies all signature of methods and reduces risk of vector's type changes
来源:https://stackoverflow.com/questions/9667292/refactoring-a-class-which-inherited-from-a-stdcontainer