I have a base interface and a few classes derived from it, holding similar but different data. I need a few arrays of same derived classes and would like to hold the pointers to these arrays in another array.
I want to keep the derived objects in the same array (not allocated separately) for performance reasons.
class B {
public:
virtual void whoami( void ) = 0;
virtual ~B() = default;
};
class B1 : public B {
public:
void whoami(void) { std::cout << "[I'm B1]\n"; };
};
class B2 : public B {
public:
void whoami(void) { std::cout << "[I'm B2]\n"; };
};
I would like to have all vectors of similar types packed in a separate vector like this:
void test_vector_of_vectors() {
std::vector<std::shared_ptr<std::vector<B>>> vv; // vector of shared pointers to vectors
auto v1 = std::make_shared<std::vector<B1>>();
vv.emplace_back(v1);
auto v2 = std::make_shared<std::vector<B2>>();
vv.emplace_back(v2);
}
I understand I can push different objects in a vector of pointers to the base class. This is not possible in my case as I cannot allocate every single object on the heap. I need them contiguous per each object type.
I read those objects from HDF files in blocks of the same type. I.e. I read 200'000 B1 in a "single" read and put them in a vector, then I read another 500'000 B2 in a single read and put them in a vector.
Then I need to iterate over the two (or n) vectors but I don't know how many "vectors" are there or what type of derived class each vector contains.
I could use a union class to hold all types and allocate an array of arrays of this union class. However this will be one more copy operation (reading from hdf and transforming to the union) and waste of space/memory (as some objects occupy a lot more bytes than others).
I could also resort to old plain C and use switch statements. Not ideal.
I apologize for my bad behavior and not answering back. I felt I was getting nowhere and totally forgot to check the thread.
I actually like the variant solution. I did not think about it, but it is simple, readable and elegant.
At the end, I adopted another solution. I defined the interface for the functionality I needed for the vector (container) as a virtual base class. Then I derived all specialized classes via a template. I'm instantiating the specialized classes and putting them in a vector or pointers to the base class, like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
class B {
virtual B();
virtual ~B() = default;
virtual ....
}
template<class T>
class Bcontainer : B {
std::vector<T> V ;
}
void test() {
std::vector<std::shared_ptr<B>> vv;
vv.emplace_back(std::make_shared<Bcontainer<B1>>());
vv.emplace_back(std::make_shared<Bcontainer<B2>>());
}
Not exactly nice. I needed a lot of functionality in the container itself so perhaps I can live with this. Also speed is of the essence, so perhaps templatized classes help (not sure about this, need to test).
Templated classes don't make any difference in performance.
Templated classes can lead to code bloat if you have a lot of variations of a templated class, but there is no performance impact.
You want to watch out for the standard performance pitfall for a vector.
Vectors expand as necessary when you reach the capacity of the vector.
This means a new underlying array is allocated and the existing array is copied to the new array every time the vector expands. You can avoid this if you have a good idea of how large the vector needs to be by calling reserve(<size>).
class B {
public:
virtual ~B() = default;
};
template <class T>
class Bcontainer : public B {
std::vector<T> V;
};
class B1 : public Bcontainer<B1> {};
class B2 : public Bcontainer<B2> {};
void test() {
std::vector<std::shared_ptr<B> > vv;
vv.emplace_back(std::make_shared<Bcontainer<B1>>());
vv.emplace_back(std::make_shared<Bcontainer<B2>>());
}
> Having a shared_ptr to a vector usually doesn't make too much sense.
for example: a vector of points that represents a mesh is shared between several objects of different classes.
now, in OP case I don't see several objects, just one that holds arrays to these derived objects
¿so why use shared_ptr at all?
¿couldn't simply use std::vector< container >?
may use opencv cv::Mat for ideas (the images may change the pixel type at runtime)
Yes, reading HDF files, I have an exact number of rows I know I'll read, so the vectors are sized accordingly upfront.
I use shared pointers to derived objects, as I'm not sure (not tested) if I can simply use derived classes in a std::vector<BaseContainer> vector. Can I?
Thanks for the hint on cv::Mat. I'll have a read at that.
I'm not sure (not tested) if I can simply use derived classes in a std::vector<BaseContainer> vector. Can I?
Techincally, it's legal, but you don't get polymorphism. The objects stored in a std::vector<BaseContainer> are onlyBaseContainer objects; you can't then use them as derived class objects.
This is called object slicing.
If you want polymorphism - i.e. you want the program to know what the real derived class types are of the objects - you need to use pointers to BaseContainer, as you are doing.