Home > front end >  How to use virtual functions of derived functions with different set of inputs from a base method in
How to use virtual functions of derived functions with different set of inputs from a base method in

Time:11-30

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.

  • Related