Re: Deriving from concrete types
kanze wrote:
Gene Bushuyev wrote:
<snip>
Inheriting from a large concrete class like std::vector is a
gratuitous violation of design rules, which has only problems
and no real advantages.
... There is also no doubt in my mind that
std::vector<> was not so designed, and that any use of it as a
public base class will run into problems. Not because it
doesn't conform to the rules of the OO design concept, per se,
but because it doesn't conform to the rules of any design
concept which would normally be implemented using it as a base
class in C++.
It's funny. When I first added my $0.02 to this discussion,
I didn't have a concrete example. I was merely responding
to what I saw as overly rigid dogma.
However, just in the last hour, I find myself writing a class
which is publicly derived from std::vector .
The situation is that I have a class to implement a configuration
table. The (private) member object that holds the table data was
originally
typedef std::vector<Entry> KeywordData
std::vector<KeywordData> tableData;
(If anyone cares, Entry is basically a bag of data.)
Another part of the class maps the keyword to the index in
"tableData".
Writing the code to set up and to access the data went
quickly, because I (and anyone who maintains the code)
know all about how to use std::vector<> (well, almost all....)
I then went to write my "print()" function, which prints out
the contents of the table for debugging purposes,
(one of my design principles: never create a complicated
data structure without a "print()" function!) and
realized that "tableData" didn't have the keyword.
At first, in line with the suggestions in this thread, I tried
making KeywordData a class:
struct KeywordData { std::vector<Entry> data; std::string keyword; };
but since I wanted to preserve the interface, I found myself
writing forwarding functions for large parts of the std::vector
interface, with frequent trips to the std::vector man page to
get all the types just right.
This violated my personal design principle of not
reimplementing the wheel.
I then tried using private inheritance, but found myself
cut-and-pasting and "awk"ing from Sun's man pages
to get the many, many "using" statements.
This violated my design principle of avoiding
cut-and-paste. (Anyone who has had to maintain
code with large amounts of cut-and-pasted code will
understand this.)
I then thought of moving "keyword" to a separate vector,
but this violated my design principle of keeping related
data together. (20 years of Fortran programming really
soured me on emulating structs by using parallel data
structures.)
So, in accordance with *my* design principles, developed
over 30 years of large-scale software development and
maintenance, led me to do:
struct KeywordData : public std::vector<Entry> { std::string keyword;
};
std::vector<KeywordData> tableData;
So far, given how I use the class, I don't see a problem.
It's quick to write, economical in concepts, requires no change
to the part of the code that expected a std::vector, the meaning
is obvious to anyone who reads the code, and the "gotchas"
are obvious, at least to anyone who reads this newsgroup :-)
And, short of spending a few days (which I can't afford) coming
up with a different design that satisfies the OOD crowd, I don't
see a better, more obvious design.
-- Alan McKenney
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]