C++ oop array query

198 Views Asked by At

Is it possible to have a single container which can be used to store "all objects". In other words, is it possible to use a container to store objects of multiple, arbitrary types, in C++?

I want a single container type which I can use to insert multiple objects of different types.

I need one container which can be used to store all objects. I want to avoid having to use independent containers for each type of object.

Is this possible and if so how can this be done?

Further, if the container is a dynamic 2-dimensional container, how can one initialize it? Where should it be declared?

4

There are 4 best solutions below

13
FreelanceConsultant On

Yes, this is possible in C++.

The easiest solution is to use std::any.

I'm not sure why you are asking about static variables however. Your array containing elements of type std::any need not be static.

You would also probably prefer to use a std::vector rather than an array.


Clarification

If you want something which you can access using 2 dimensions, you can easily accomplish thing using a vector of a vector.

std::vector<std::vector<TYPE>> my_2d_container;

// You initialize it like this:

// Create a vector
std::vector<TYPE> tmp;
tmp.push_back(... something ...); // repeat for each element

// Move the vector in the vector of vectors
// Note using `std::move` is an optimization. You can't use `tmp`
// again after doing this because the memory it references
// (effectively) no longer exists. But this is really quite advanced
// C++, so you could remove the `move` operation
my_2d_container.push(std::move(tmp));

To use std::any is easy. You just use std::any_cast in a try-catch, and deal with instances of std::bad_any_cast which tells you when you are trying to get an object of the wrong type.

See the docs for more information. https://en.cppreference.com/w/cpp/utility/any

You could probably also explicitly check the type using the type() function as part of a if statement. This might be faster. My preference is for the try-catch.


Further information and polymorphism

There's also ways to accomplish what you want using polymorphism. In this case, everything you want to insert into the container has to inherit from some base type.

Whether or not this is a good idea for your particular use case is heavily dependent on the details of what you are doing. In general, you cannot and should not implement a pattern like this because you will end up with all sorts of strange code behaviour whereby multiple unrelated types all inherit from the same base type. Hence, you should use the std::any type-erasure solution which is already provided to you.

There are also more advanced type-erasure techniques, but in order to implement such things you really need to be very familiar with design patterns theory. This is really not easy stuff.

Alternatives: Use another language

C++ is a strongly typed, statically typed language. It does not provide easy to use language features to support what you want to do.

What you really want is a dynamically typed language, such as python, which will easily allow you to implement the pattern you want to implement.

Alternatives would be Java, where every object inherits from Object. So while Java is a statically typed language, it is intrinsically much easier to what you want in Java, compared to C++.

The reason C++ is the way it is? Speed. The compiler can produce highly optimized code because all the types you use are minimal in their memory footprint/size. If you take the polymorphic or type erasure approach, expect a performance penalty. But this probably isn't something you care about.

6
Otávio Augusto Silva On

An easy way to do it is to create a reference to it in the class variables, and pass the address to the array in the constructor. Something like:

class example
{
private:
    double **v;
public:
    example(double **v);
    void insert(double element, unsigned i, unsigned j);
};

example::example(double **v)
{
    this->v = v;
}

void example::insert(double element, unsigned i, unsigned j)
{
    v[i][j] = element;
}

And your main function needs to allocate the array properly:

auto v = new double*[4];
for (unsigned i = 0; i < 4; i++)
{
    v[i] = new double[4]();
}

And then you can pass the pointer to the objects when creating them:

auto e1 = example(v);
auto e2 = example(v);

And then you can insert elements to it:

e1.insert(2.444456, 1, 3);
e2.insert(4.566, 2, 1);

Also, don't forget to free the memory before closing the program:

for (unsigned i = 0; i < 4; i++)
{
    delete[] v[i];
}
delete[] v;

Give it a try.

2
Alijvhr On

Yes, Using Static members!

You can use static members of class. Check this link for more info and learning. It can be used in anywhere including inside other object's methods!

For showing you a demo you can consider code below:

#include <iostream>
#include <vector>

class Box {
public:
    static std::vector<std::vector<int>> Some2DVector;
};

std::vector<std::vector<int>> Box::Some2DVector; // initialize to use

int main(){
    std::vector<int> test = {1,2,3};
    Box::Some2DVector.push_back(test);
    std::cout<<Box::Some2DVector.size(); //prints 1
    std::cout<<Box::Some2DVector[0].size(); //prints 3
    std::cout<<Box::Some2DVector[0][1]; //prints 2
    Box::Some2DVector[0][1] = 7;
    return 0;
}

Explanations

This code uses a 2dimensional int vector. you can learn about vectors here. Simply they are flexible arrays. Their usage is also like arrays!

As a static member you can call it where ever you want using the class name it self! It does not need an instance to use.

Note that you should initialize a static member before using it!

As you can see you can use same vector in everywhere globally. You can use any type as static member it's just a variable bound to a class name to be found easier!

7
Hari On

A std::vector is a very convenient way to implement a dynamic array as per your requirements. Elements can be added and removed dynamically from a vector. Also, you don't have to worry about memory allocation and de-allocation (read about RAII). You could readily create a 2D array using vectors, also.

Also, make it a static data member of the class, so that all objects of that class can access a single static data member.

In case of a 2D array, the class definition in the header (.h) file looks like:

class MyClass {
  // other data members and functions.

  static std::vector<std::vector<int>> my_2d_data;
};

Such a static data member needs to be explicitly defined in the source (.cpp) file:

std::vector<std::vector<int>> MyClass::my_2d_data;

See here to know more about static members. A static member undergoes value initialization whenever the member is defined. See here for a discussion about default and value initialization of vectors. A static member variable is initialized in the same manner (i.e., value initialized) as a global (i.e., non-local) variable.

Once a static data member is initialized, it can be used in whatever manner you want to use it for, just like any variable. In the case of your 2D vector, at some point in your code, you could reserve memory for the outer dimension in this way, my_2d_data.reserve(num_rows), if you know the number of rows that your 2D vector is going to have. Then you could push_back 1D vectors of size num_columns as and when each of the rows are ready. This is just one way to update your 2D vector. You can do it according to your requirement.

Also, in the above class definition, the static member is declared as a private member. That would be convenient to make it accessible to objects of that class and nobody else (other than class member functions and friends of the class).

The element type in the vector was chosen as int for illustrative purposes. You can choose the type as needed.