An array of pointers to arrays of derived objects?

Hello,

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);
}

How would you suggest me to fix this?
Having a shared_ptr to a vector usually doesn't make too much sense. Why not a pointer to the objects?

This:

std::vector<std::shared_ptr<std::vector<B>>> vv; // vector of shared pointers to vectors

tries to create objects from abstract classes. That doesn't work. It looks like you want this:

std::vector<std::vector<std::shared_ptr<B>>> vv;

Note that you cannot push_back() a vector of the wrong type like B1.
Ignoring shared_ptr and just looking at pointers:

1
2
std::vector<B1*> vb1;
std::vector<B2*> vb2;


vb1 and vb2 are of completely unrelated types.

If you have:

 
std::vector<std::vector<B*>> vv;


then:

1
2
// vv.emplace_back(vb1);    // NO
// vv.emplace_back(vb2);    // NO 


is invalid as vb1 and vb2 have unrelated types to vv type. So can't be used here.

You can do this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <vector>
#include <iostream>

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"; };
};

int main()
{
	std::vector<B*> vb;

	B1 b1;
	B2 b2;

	vb.emplace_back(&b1);
	vb.emplace_back(&b2);
}


as b1, b2 types are related to the type of vb.

Last edited on
Hi,

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.
You can do this using std::variant:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <variant>
#include <iostream>
#include <vector>

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"; };
};

void test_vector_of_vectors() {

	using V1 = std::vector<B1*>;
	using V2 = std::vector<B2*>;
	using V = std::variant<V1, V2>;

	std::vector<V> vv;

	auto v1 = V1();
	vv.emplace_back(v1);

	auto v2 = V2();
	vv.emplace_back(v2);
}

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>).
Last edited on
your code snip, but compilable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 only BaseContainer 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.
Topic archived. No new replies allowed.