Home > Blockchain >  How to create a vector of base class where derived classes have different structs
How to create a vector of base class where derived classes have different structs

Time:04-28

I wonder if someone could explain the solution to this...

Top class has a vector of Base class. Each derived class of Base class has a different data struct. Base class has a vector of the derived class data struct. Top class needs to access the size of the struct footprint and well as the size of the struct vector and the underlying array.

Top class is blind to the derivatives of the base class.

If I template then Top class needs to be template aware and this will not work. When I derive, top class only see base class. I am not able to make the struct virtual ( this would have been the ideal solution. )

The derived class needs to define, or layout, the struct. Top class needs access to underlying array.

How can I solve this?

#include <iostream>
#include <string>
#include <vector>

class Base {
public:
    struct Data {
    };
    Data data;
    std::vector<Data> myData;
};

class Foo1 : public Base {
public:
    struct Data {
        std::string s1;
        std::string s2;
        std::string s3;
    };
};

class Foo2 : public Base {
public:
    struct Data {
        int i1;
        double d2;
        std::string s3;
    };
};

class Top {
public:
    std::vector<Base> foos;
    void test() {
        for (std::vector<Base>::iterator f = foos.begin(); f != foos.end(); f  ) {
            std::cout << "Data Size = " << sizeof((*f).data) << std::endl;
        }
    }
};

int main(int argc, char* argv[])
{

    std::cout << "Test Begin" << std::endl;

    Foo1 f1;
    Foo2 f2;
    Top t;

    t.foos.push_back((Base)f1);
    t.foos.push_back((Base)f2);
    
    t.test();

    std::cout << "Test End" << std::endl;
}

CodePudding user response:

class Base {
public:
    struct Data {
    };
    Data data;
    std::vector<Data> myData;
};

This declares a class, Base, with an inner class Data which is, basically, empty, and a Data class member called data, basically an object that contains nothing.

Deriving from Base makes no difference, whatsoever, with any of this. This is what this class is, and defining some other inner class in a derived clas, that happens to have the same name, Data, doesn't change the fact that Base has a member called data that's this Data that you see right there. Derived classes in C don't work this way. All they do is define "things" in addition to what they inherit from the base class. The only thing they override and reimplement, in a general sense, would be any virtual methods.

std::vector<Base> foos;

In addition to all of that, this is a vector of Base objects. Putting some derived class in here will result in object slicing, leaving with only Base classes, in the vector.

Object slicing is adequately explained in many other questions here, so I'll refer you to them for more information. But in your case you'll also need to rethink your overall class design:

class Foo1 : public Base {
public:
    struct Data {

All this does is define a new class, Foo1::Data. Base::Data is still what it always was, an empty class, and data in the Base is still a Base::Data, nothing's changed about it.

How can I solve this?

It's really not possible to give an accurate answer to this without a complete understanding of how all these classes are intended to be used. Here are just several possibilities:

  1. Just declare a Data in each derived class, and put data in the derived class

  2. Use virtual methods for everything, with Data being just an internal, private, implementation detail of each derived class

  3. Some combination of these two

In all cases you'll also have to deal with the vector's object slicing problem, by using pointers, or, even better, std::unique_ptr or std::shared_ptr, and having a vector of those.

CodePudding user response:

Thank you for the inspiration. I have a working solution posted below.

No implementation in the base class so it is now only a virtual function holder so a little bit of code bloating incurred.

Not sure if this solution will incur some caveat down the road but at least I have something working for my initial needs. Many Thanks for the help.

#include <iostream>
#include <string>
#include <vector>

class Base {
public:
    virtual void getStructInfo(int&, int&, std::uintptr_t&) = 0;
};

class Foo1 : public Base {
public:
    struct Data {
        std::string s1;
        std::string s2;
        std::string s3;
    };
    std::vector<Data> myData;
    void getStructInfo(int& _sizeof, int& _count, std::uintptr_t& _address) {
        _sizeof = sizeof(Data);
        _count = myData.size();
        _address = (std::uintptr_t)myData.data();
    }
};

class Foo2 : public Base {
public:
    struct Data {
        int i1;
        double d2;
        bool b3;
    };
    std::vector<Data> myData;
    void getStructInfo(int& _sizeof, int& _count, std::uintptr_t& _address) {
        _sizeof = sizeof(Data);
        _count = myData.size();
        _address = (std::uintptr_t)myData.data();
    }
};

class Top {
public:
    std::vector<Base*> foos;
    void test() {
        int size;
        int count;
        std::uintptr_t address;
        for (std::vector<Base*>::iterator f = foos.begin(); f != foos.end(); f  ) {
            (** f).getStructInfo(size, count, address);
            std::cout << "Data Size = " << size << " Count = " << count << " Address=" << std::to_string(address) << std::endl;
        }
    }
};

int main(int argc, char* argv[])
{

    std::cout << "Test Begin" << std::endl;

    Foo1 f1;
    Foo1::Data f1d;
    f1d.s1 = "hello1";
    f1d.s2 = "hello2";
    f1d.s3 = "hello3";
    f1.myData.push_back(f1d);
    f1d.s1 = "goodbye1";
    f1d.s2 = "goodbye2";
    f1d.s3 = "goodbye3";
    f1.myData.push_back(f1d);
    Foo2 f2;
    Foo2::Data f2d;
    f2d.i1 = 10;
    f2d.d2 = 1.9;
    f2d.b3 = true;
    f2.myData.push_back(f2d);

    Top t;
    t.foos.push_back(&f1);
    t.foos.push_back(&f2);
    t.test();

    std::cout << "Test End" << std::endl;
}

result =

Test Begin
Data Size = 120 Count = 2 Address=1886816049088
Data Size = 24 Count = 1 Address=1886816032176
Test End
  • Related