Pedro Piacenza
Pedro Piacenza

Reputation: 5

Passing arguments to Base constructor using Derived class member variables in C/C++

I am building a class to control some hardware which has a few different versions. Because of this variety of versions, certain parameters need to be hardcoded, and they are different for each version. However, beyond those hardcoded parameters, all versions provide the same funcionality.

The way I thought I would architect this was to use a base class which contains all the methods needed, and derived classes (DerivedV1, DerivedV2, etc) where I would just define these hardcoded parameters as member variables and then pass them as constructions parameters to the base class.

Here is a minimal example of the code: (this is deployed on a microcontroller hence the use of arrays instead of vectors, also disregard the use of std::cout, it was only included here to illustrate the problem)

#include <iostream>
using namespace std;

void print_array(uint16_t *array, uint16_t size){
    cout<<"[ ";
    for(int i=0; i < size-1; i++){
      cout<<array[i]<<", ";
    }
    cout<<array[size-1]<<" ]"<<endl;
}


class BaseClass{
protected:
  std::string id_;
  uint16_t num_cats_;    
  uint16_t num_dogs_;     
  uint16_t *cat_mapping_;
  uint16_t *dog_mapping_; 
  uint16_t combinations_;

public:  
    BaseClass(string id, uint16_t num_cats, uint16_t num_dogs,
            uint16_t *cat_map, uint16_t *dog_map){

        cout<<"Base Constructor"<<endl;
        id_ = id;
        num_cats_ = num_cats;
        num_dogs_ = num_dogs;
        cat_mapping_ = cat_map;
        dog_mapping_ = dog_map;
        combinations_ = num_cats_*num_dogs_;
        cout<<"Num cats: "<<num_cats_<<endl;
        cout<<"Num dogs: "<<num_cats_<<endl;
        print_array(cat_mapping_, num_cats_);
        print_array(dog_mapping_, num_dogs_);
        cout<<"Combinations: "<<combinations_<<endl;
    }

    virtual ~BaseClass(){};

};


class DerivedClassV1 : public BaseClass 
{
private:
  uint16_t num_cats_ = 10;
  uint16_t cat_map_[10] = {31, 15, 20, 32, 13, 25, 19, 16, 28, 23};
  uint16_t num_dogs_ = 8;
  uint16_t dog_map_[8] = {5, 25, 23, 4, 13, 15, 14, 26};

public:
  DerivedClassV1(string id) : BaseClass(id, num_cats_, num_dogs_, cat_map_, dog_map_){
    cout<<"Derived Constructor";
  }
};


int main()
{
    DerivedClassV1 dummy("v1");
    return 0;
}

Execution of this code results in garbage being output:

Base Constructor
Num cats: 64
Num dogs: 64
[ 0, 0, 2, 0, 0, 0, 4781, 64, 0, 0, 124, 0, 0, 0, 0, 0, 0, 0, 4704, 64, 0, 0, 3040, 64, 0, 0, 42640, 13254, 32766, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44869, 10268, 32576, 0, 0, 0, 0 , 0, 42648, 13254, 32766, 0, 0, 0, 1, 0, 3456, 64, 0, 0, 0, 0, 0, 0, 13708, 48499 ]
[ 0, 0, 0, 0, 0, 0, 0, 4704, 64, 0, 0, 3040, 64, 0, 0, 42640, 13254, 32766, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44869, 10268, 32576, 0, 0, 0, 0, 0, 42648, 13254, 32766, 0, 0, 0, 1, 0 , 3456, 64, 0, 0, 0, 0, 0, 0, 13708, 48499, 5513, 17381, 3040, 64, 0, 0, 42640, 13254, 32766, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13708, 63219, 29188, 48153, 13708, 57481, 17840, 484 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4704, 64, 0, 0, 42648, 13254, 32766, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3040, 64, 0, 0, 42640, 13254, 32766, 0, 0, 0, 0, 0, 3081 ]
Combinations: 7936
Derived Constructor

What am I doing wrong here? Why are the arguments sent to the BaseClass not the correct ones defined in the derived class?

Should I be doing this differently? Any help is appreciated

Upvotes: 0

Views: 127

Answers (2)

QuentinUK
QuentinUK

Reputation: 3077

You can pass the DerivedClassV1 values to the BaseClass by using a template. But the values have to be static constants so they're initialized before the base class.

#include <iostream>
using namespace std;

void print_array(const uint16_t *array, uint16_t size){
    cout<<"[ ";
    for(int i=0; i < size-1; i++){
      cout<<array[i]<<", ";
    }
    cout<<array[size-1]<<" ]"<<endl;
}


template <class T>
class BaseClass {
protected:
  std::string id_;
  const uint16_t *cat_mapping_ = T::cat_map_;
  const uint16_t *dog_mapping_ = T::dog_map_; 
  uint16_t combinations_;

public:  
    BaseClass(string id){

        cout<<"Base Constructor"<<endl;
        id_ = id;
        combinations_ = T::num_cats_*T::num_dogs_;
        cout<<"Num cats: "<<T::num_cats_<<endl;
        cout<<"Num dogs: "<<T::num_dogs_<<endl;
        print_array(cat_mapping_, T::num_cats_);
        print_array(dog_mapping_, T::num_dogs_);
        cout<<"Combinations: "<<combinations_<<endl;
    }

    virtual ~BaseClass(){};
};


class DerivedClassV1 : public BaseClass<DerivedClassV1> 
{
public:
  static const uint16_t num_cats_ = 10;
  static constexpr uint16_t cat_map_[10] = {31, 15, 20, 32, 13, 25, 19, 16, 28, 23};
  static const uint16_t num_dogs_ = 8;
  static constexpr uint16_t dog_map_[8] = {5, 25, 23, 4, 13, 15, 14, 26};

public:
  DerivedClassV1(string id) : BaseClass(id){
    cout<<"Derived Constructor";
  }
};
constexpr uint16_t DerivedClassV1::cat_map_[10];
constexpr uint16_t DerivedClassV1::dog_map_[8];

Upvotes: 0

Bathsheba
Bathsheba

Reputation: 234705

The behaviour of your program is undefined.

Conceptually, DerivedClassV1::cat_map_ &c. do not exist at the point the base class is constructed.

A pointer to such an array (e.g. cat_map in the base class constructor) is effectively dangling.

Can't you use polymorphism to yield the appropriate arrays?

Upvotes: 2

Related Questions