Why I can not use abstract class in std::vector? - c++

I have came from those questions:
Why can't we declare a std::vector<AbstractClass>?
abstract classes in std containers
How to store a vector of objects of an abstract class which are given by std::unique_ptr?
They all suggested that I should use pointer or smart pointers instead.
As far as I know, Data are dynamically allocated in std::vector which means that there are pointers internally in the std::vector. So why I can not use abstract classes directly? why I have to use pointers(The one I specified) for pointers(the internally) in order to use abstract classes with std::vector. I know some features like std::vector::resize won't work. However, std::vector::reserve and std::back_inserter will solve the problem.

As far as I know, Data are dynamically allocated in std::vector which means that there is pointers internally in the std::vector
That's absolutely right. However, the pointer (actually, one of two pointers in the very common implementation) points to an array of identically-sized elements of type T, not to a single element of T or its subtype. Essentially, the pointer is used to represent an array, not to refer to a class or its subclass.
That is why you need an extra level of indirection - elements of an array T[] are not capable of storing subclasses of T without object slicing.

Data are dynamically allocated in std::vector which means that there is pointers internally in the std::vector
No, that's a misconception. std::vector allocates an array of instances internally, and you can't create an instance of an abstract class.

While it is true that memory for std::vector is allocated dynamically, the fact is that in std::vector<T> objects of type T are stored as copies. And you simply cannot copy abstract classes.
For example, if you have a base class A, a derived class B, and a std::vector<A>. Then, if you try to stored an object of type B in that vector, it will be copied and stored as an A. That is, it will be spliced.
class A {};
class B: public A {};
int main()
{
std::vector<A> as;
B b;
as.push_back(b); //<-- splice!!!
}
That, assuming that class A is copiable (and non-abstract). If it is abstract the compiler will save you the trouble and fail the declaration of the vector.

In order to insert something into a vector, the template has to first instantiate the class (since it uses the copy constructor). Since this is not possible with an abstract class, you can't do it!
Source

Related

How can I pass and store an array of variable size containing pointers to objects?

For my project I need to store pointers to objects of type ComplicatedClass in an array. This array is stored in a class Storage along with other information I have omitted here.
Here's what I would like to do (which obviously doesn't work, but hopefully explains what I'm trying to achieve):
class ComplicatedClass
{
...
}
class Storage
{
public:
Storage(const size_t& numberOfObjects, const std::array<ComplicatedClass *, numberOfObjects>& objectArray)
: size(numberOfObjects),
objectArray(objectArray)
{}
...
public:
size_t size;
std::array<ComplicatedClass *, size> objectArray;
...
}
int main()
{
ComplicatedClass * object1 = new ComplicatedClass(...);
ComplicatedClass * object2 = new ComplicatedClass(...);
Storage myStorage(2, {object1, object2});
...
return 0;
}
What I am considering is:
Using std::vector instead of std::array. I would like to avoid this because there are parts of my program that are not allowed to allocate memory on the free-store. As far as I know, std::vector would have to do that. As a plus I would be able to ditch size.
Changing Storage to a class template. I would like to avoid this because then I have templates all over my code. This is not terrible but it would make classes that use Storage much less readable, because they would also have to have templated functions.
Are there any other options that I am missing?
How can I pass and store an array of variable size containing pointers to objects?
By creating the objects dynamically. Most convenient solution is to use std::vector.
size_t size;
std::array<ComplicatedClass *, size> objectArray;
This cannot work. Template arguments must be compile time constant. Non-static member variables are not compile time constant.
I would like to avoid this because there are parts of my program that are not allowed to allocate memory on the free-store. As far as I know, std::vector would have to do that.
std::vector would not necessarily require the use of free-store. Like all standard containers (besides std::array), std::vector accepts an allocator. If you implement a custom allocator that doesn't use free-store, then your requirement can be satisfied.
Alternatively, even if you do use the default allocator, you could write your program in such way that elements are inserted into the vector only in parts of your program that are allowed to allocate from the free-store.
I thought C++ had "free-store" instead of heap, does it not?
Those are just different words for the same thing. "Free store" is the term used in C++. It's often informally called "heap memory" since "heap" is a data structure that is sometimes used to implement it.
Beginning with C++11 std::vector has the data() method to access the underlying array the vector is using for storage.
And in most cases a std::vector can be used similar to an array allowing you to take advantage of the size adjusting container qualities of std::vector when you need them or using it as an array when you need that. See https://stackoverflow.com/a/261607/1466970
Finally, you are aware that you can use vectors in place of arrays,
right? Even when a function expects c-style arrays you can use
vectors:
vector<char> v(50); // Ensure there's enough space
strcpy(&v[0], "prefer vectors to c arrays");

c++ array of interfaces that contains class instances

How is an array that stores multiple IClass objects, into which we add instances of Class objects that implements IClass gets stored in memory? I am trying to confirm if my assumption is right (in case of 32 bit app)
we have an array of 32 bit pointers to the IClass objects that have a 32 bit pointer to the actual instance object of Class that takes up sizeof(Class) in memory? Also, if an interface doesnt have any virtual/abstract methods it only has a 32 bit pointer at its root and thats it?
Is this right?
as always, any input appreciated
edit:
say we have the following definitons:
class Class : IClass
{
int foo();
int bar;
}
class IClass
{
virtual int vfunc();
}
// array def:
IClass arr[1337];
I begin storing Class in the array arr, how does the runtime store sizeof(Class) into something that have sizeof(IClass) allocated for it?
how does the runtime store sizeof(Class) into something that have sizeof(IClass) allocated for it?
It does not. By assigning a Class instance to an element of the IClass array you are effectively invoking the IClass copy-constructor and copying just the parent part of the object. This is called "slicing", as explained in this answer.
In order to avoid it, you need to hold pointers (or references) to IClass. Three problems arise then:
You cannot copy the actual values, only move them or reference them, unless you implement a virtual "clone" method for your class hierarchy.
You need to handle ownership: an array of class C owns its instances, but an array of C* or C& does not. Normally you would want to use unique_ptr<T> or shared_ptr<T>, depending on the lifetime that is expected for the instances and who will see them.
You need a way to polymorphically construct and destroy the elements. The latter is easy, you just declare the destructor virtual at the base class. The former usually involves either somebody else passing you the instances they want saved (e.g. using an unique_ptr<IClass> that gives you the ownership) or creating some sort of factory infrastructure.

List (vector) of different structs

is it possible to have some kind of list / array / vector of different structs?
For example in MFC there are CObject and CArray but without MFC:
I can do something alike
std::vector<void*> pVec;
{
MYSTRUCT m = new MYSTRUCT;
pArr.push_back(m);
// looks fine here
}
//what is in my vector now?
Is there something that can handle it?
The obvious preferred approach is to have a std::vector<Base*> to handle polymorphic types.
If you really have completely unrelated types, as it seems to be the case, then boost::any or boost::variant type erasers might be what you are looking for :
std::vector< boost::variant<int, std::string> > vec;
vec.push_back( 21 );
vec.push_back( "hello " );
The example that you have given is dangerous. How do you clean up (i.e. delete) what you've allocated? Its type has been erased by being assigned to a void *, so how can the compiler know which operator delete to call?
You can safely go about this at least two different ways:
All the objects can inherit from a common base which has a virtual destructor. You can then make a std::vector of std::unique_ptrs to the common base. This has the advantage of being extensible (you can declare new types which derive from the common base and throw them in the container without modifying the container), but has the disadvantage of requiring that you go to the free store (to allocate memory for the std::unique_ptrs to point to).
You can use a variant type like boost::variant. This will not require a common base, or allocations on the free store, but it will require that you declare all the types that can possibly be stored in the container when you declare the container (i.e. it's not as easily extensible as an inheritance-based solution).

Unhandled exception - c++ program stops on cast

I have several structs:
struct Token
{
//some content
}
Then follows a bunch of structs that inherit from Token:
struct A : public Token{
//Stuff
}
.
.
.
struct Z : public Token{
//Other stuff
}
I have a vector std::vector filled with subclasses A through Z and my program crashes when I try to cast any element in the the vector to the subclass. I'm casting by doing the following:
A subclass = *((A * ) &vector[0]);
What am i doing wrong?
You should use dynamic_cast when casting pointers from one type to another in your use case.
The one you are using is a C style cast and I strongly suggest you to go with a dynamic_cast.
So your code should look something like:
if(dynamic_cast<A *>(vector[0]))
A subclass = *(dynamic_cast<A *>(vector[0]));
When a dynamic_cast fails it will return a NULL pointer and you should take care of it appropriately.
Refer dynamic_cast and static_cast in C++ for more information.
Additionally When should static_cast, dynamic_cast, const_cast and reinterpret_cast be used? will help you understand a lot more types of casts.
A meaningful usage would be
A &subclassref = vector[0]);
In above line, no new object is created.
BTW what is the type of your vector and what exactly do you want to achieve? If you store objects of type A to Z in a single vector, it may at some point of time suffer object slicing.
This answer may be wrong because I'm making a guess as to how you have filled the std::vector<>.
You simply cannot put objects of subclasses into an std::vector<Base>. All objects in an std::vector<Base> are precisely of type Base. If you try something like this:
std::vector<Base> myVec;
myVec.push_back(Derived1(...));
you first construct an object of class Derived1 which is subsequently spliced into an object of class Base, i. e. a new object of class Base is copy-constructed from the derived object. Only this copy constructed base class object ends up in the std::vector<>.
If you want to have an std::vector<> of polymorphic objects, you must use a pointer type as the template argument (and consequently allocate the contained objects with new).

Proper initialisation of smart pointers array

For such case:
class A
{
//implementation
};
class B
{
public:
B();
~B();
private:
std::vector<std::shared_ptr<A>> _innerArray;
};
what should I do in the B() to create an object with valid state? Do I need to manually call default constructor for every A object in array? And do I need to do something special in ~B()? If B class is example of bad design, feel free to say how to make it better. Thanks.
Edit
So, here is a scheme of what I really need here.
So real values stored only in array of A and all other objects are for storing connections.
The easiest example - A = dot, B = Line (or curve) going via selected dots and C = a plane described by lines. Hope it makes question more exact.
To create a B object in a valid state you do not have to do anything more. You even do not have to declare and implement constructor and destructor for B. std::vector<std::shared_ptr<A>> that is a member of B will be default initialized in B's constructor which means it will not have any elements in a container yet. It will also be properly deleted in ~B thanks to std::vector and std::shared_ptr destructors.
On the other hand if you for example want to initialize it somehow (i.e. 3 values) you can use std::vector's std::initializer_list constructor in a B's constructor initialization list. For example:
class B
{
public:
B(): _innerArray{ std::make_shared<A>(),
std::make_shared<A>(),
std::make_shared<A>() } {}
~B() {}
private:
std::vector<std::shared_ptr<A>> _innerArray;
};
Remember that std::make_shared uses perfect forwarding so you pass A's constructor arguments as the function arguments and not the class object itself.
Answering your concerns about the design I would like to encourage you to first think about the exclusive ownership of members in a vector before you decide to share them.
class B
{
public:
B();
~B();
private:
std::vector<std::unique_ptr<A>> _innerArray;
};
Above implementation is more effective on many grounds. First of all it makes your design more clear on who is responsible for the lifetime of As. Next std::unique_ptr is faster because it does not demand thread safe reference counting. And last but not least it does not cost any additional memory (compared to regular C pointer) while std::shared_ptr may take tens of bytes (24-48) to store shared state data which is highly ineffective when you operate on small classes. That is why I always use std::unique_ptr as my first resort smart pointer and I only fallback to std::shared_ptr when it is really needed.
EDIT:
Answering your edit I would create 3 containers of classes A, B, C. Depending of the fact if you need them to be polymorphic or not I would store either values like that (non-polymorphic types):
std::deque<A> as;
std::deque<B> bs;
std::deque<C> cs;
or (polymorphic types):
std::vector<std::unique_ptr<A>> as;
std::vector<std::unique_ptr<B>> bs;
std::vector<std::unique_ptr<C>> cs;
in that order (as must live longer than bs and bs must live longer than cs). Then I would just have std::vector<A*> inside B class and std::vector<B*> inside C class without any smart pointers usage.
I hope that helps.
EDIT:
Changed std::vector to std::deque in the first case which allows references/pointers to container elements survive containers extensions with push_back(). However they will not survive erasing elements, sorting or other stuff.
If you do it like that, the vector has a size of zero elements, i.e. the contents are trivially properly initialized. If the vector were of positive size (e.g. after calling resize on the vector), each of the elements would be properly initialized. Since the elements are shared_ptrs, the default constructor of shared_ptr would be called, which means that you would end up with a vector of empty pointers.
If you want to copy the contents from another container, use the iterator version of the vector constructor:
B (SomeContainerTypeContainingSharedPointers container)
: _innerArray (container.begin (), container.end () ) {
}
If you do not want to initialize the vector from a container, but from somewhere else (e.g. create the objects on the fly) -- write an input iterator type yourself (i.e. kind of a "factory iterator").
The vector is empty so you don't have to do anything special in the default constructor. And you don't need to do anything in B() either. The reference count of the shared_ptrs will be decreased automatically when the vector's destructor is called.
Bt default std::shared_ptr<A> will populate inner ptr with NULL. To create smart pointer use std::make_shared:
_innerArray.push_back(std::make_shared<A>(/*constructor params here*/));
But in your example vector is empty.
The default constructor already does everything needed. You can even leave B() out without any loss.