I have a base class A, which does some preliminary stuff. There are 2 subclasses B and C, whose behaviours are slightly different from each other. There is a function in A say foo
, which is almost the same for both B and C, except one little step, which involves a function call to bar
. bar
is virtual in A and has definitions in B and C. The problem is bar
has an extra input in C. How do i handle this in the most acceptable and clean way ?
class A {
public:
A( int a, int b );
~A();
void foo( int a, int b );
virtual void bar( int a, int b );
};
class B : public A {
public:
B( int a, int b );
~B();
void bar( int a, int b );
};
class C: public A {
public:
C( int a, int b, int c );
~C();
void bar( int a, int b, int c );
}
void A::foo( int a, int b )
{
// Some code
bar( a, b );
// Some code
}
A::A( int a, int b )
{
// Some code
foo( a, b );
// Some code
}
The constructor and only the constructor calls foo
which inturn is the only function that calls bar
. I understand functions with a different signature than the base class doesnt override the virtual function in the base class. One way of doing it, is to have c
as an argument to A::foo
and A::bar
also, but i want to avoid doing it, since c
wont make much sense in case of B
. Is there a better way of passing c
cleanly to bar
?
Edit:
To give some context, class A
is a FileReader class which reads from a file, and stores it in a vector<unordered_map<int,int>>
, where every index in the vector corresponds to a record.
For class C
, instead of having just a single vector<unordered_map<int,int>>
, it was decided that it would be better if we had a vector<unordered_map<int,int>>
and a vector<size_t>
, because multiple records in the input, now belong to the same entity. Here every index in the first vector correspond to the entity
and not the record
. And the second vector maps the record
to its corresponding entity
. To find which records belong to which entity, there is an external input in the form of a big structure c
, which maps one field of the record to the entity.
Class B
was originally just class A
itself. But now since there are 2 different behaviors of the similar thing, I decided to create a separate derived class for that, and make A
a template.
CodePudding user response:
Calling virtual function from the constructor may not do what you expect. In a constructor, the virtual call mechanism is disabled because the derived class gets created last. The objects are constructed from the base up. You can find more details here: https://isocpp.org/wiki/faq/strange-inheritance#calling-virtuals-from-ctors
In this particular case class A implementation of bar() would get called instead of class C implementation of bar().
CodePudding user response:
I don’t really understand why you pass things through parameters of bar. You have already passed them through the constructors. You can change the bar signature to void bar(); and use a,b,c like class attributes.
CodePudding user response:
As already explained, that's not directly possible directly with your design. Firstly, as Rama said, a virtual function call from a constructor of a base class will call the base class function, not the derived class function.
Secondly, you are writing
void A::foo( int a, int b )
{
// Some code
bar( a, b );
// Some code
}
this call already lacks c
, where you are going to take it from?
What you should do depends on your particular situation. Following your explanation about reading from file, I would first think about a simple visitor-like pattern. As I understand, both your classes read the same data (a sequence of records), but then store it in a different way. So let the base class read raw records, and pass each record to derived classes, and let derived classes do the processing they need. Something along the lines:
void A::readAll() {
int a, b;
while (file >> a >> b) {
// a, b is the raw record as stored on disk
processRecord(a, b);
}
}
void B::processRecord(int a, int b) {
data_[a] = b;
}
void C::processRecord(int a, int b) {
size_t entityId = vector_.size();
vector_.push_back(b);
// now vector[enityId] == b
data_[a] = entityId;
}
In this case, you probably don't even need inheritance. Have a separate class (A
) for reading file, have separate classes (B
and C
) for processing data, and either store a processor pointer in A
(in this case you will probably need a base Processor
class that is inherited by B
abd C
), or store A
pointer in B
and C
(this will require reworking A
's interface).
At the same time, if, for example, you file stores a
, b
and entityId
, then you should read all three values and pass them both to subclasses, and just ignore entityId
in B
.