2

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?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
GA Haroon
  • 55
  • 2
  • If your class have static var then you can use static variables like this : class_name::your_static_var. but static variables have nothing to do with what you asked for. – Harsh Gaur Apr 15 '23 at 18:00
  • You could use a shared instance of something like `std::shared_ptr>`, though do respect C++ and the typing mechanism. Don't just cram in a bunch of random junk there. Be sure you have a proper class hierarchy. – tadman Apr 15 '23 at 18:05
  • I edited my answer and fixed code! Using static members is a wise option! check it out! – Alijvhr Apr 16 '23 at 15:24
  • You appear to have edited the title to suggest this question is about something different to your original question. You appear to be asking about several things at once: 1: How to store multiple types in the same container. 2: How to share data with global variables. 3: How to create a container which is accessed with a pair of indices (2 dimensions), rather than 1. Which of these is of most immediate importance? – FreelanceConsultant Apr 16 '23 at 15:34
  • Sorry that's my mistake. Someone else edited the question title, I believe to change the question into something it isn't intended to be. I've rolled it back. – FreelanceConsultant Apr 16 '23 at 15:35
  • I have re-written your question to make it clearer, because this is an interesting and important topic, which is relevant to modern C++, since we now have standard library methods of achieving what you describe here. If I missed something, please amend my edit. – FreelanceConsultant Apr 16 '23 at 15:40
  • Actually, what would help significantly is if you could give a short example code, even if it is pseudocode, so that we can get a full clarification of the specific problem you want to solve. Others are under the impression that `std::any` won't work for your use case. They are quite obviously wrong - however if you can clarify the problem you want to solve I can then demonstrate exactly why they are wrong. – FreelanceConsultant Apr 16 '23 at 15:44
  • @FreelanceConsultant sorry I didn't read your comments! you didn't mention me! actually he OP wrote this: "array to be inputting data into". you changed whole content to your suggestion! please let the OP decide! the OP didn't say anything about container! he just wanted a shared variable and mentioned static members! – Alijvhr Apr 16 '23 at 18:47
  • @FreelanceConsultant As you said in you profile "READ THE QUESTION". You definitely have more experience than me. I'm just seeing it other way! – Alijvhr Apr 16 '23 at 18:49
  • @FreelanceConsultant The question you want has answers already!https://stackoverflow.com/questions/4738405/how-can-i-store-objects-of-differing-types-in-a-c-container – Alijvhr Apr 16 '23 at 19:08
  • @Alijvhr I don't see what your point is. Does OP know about that question? Probably not. So why would you think the existence of it automatically dictates a particular interpretation of this question? It doesn't. If I misinterpreted, then he should clarify and edit it again. – FreelanceConsultant Apr 17 '23 at 08:06
  • @FreelanceConsultant My point is why bother to edit! Just flag it as a duplicate... My friend you just made it a duplicate! – Alijvhr Apr 17 '23 at 10:27
  • 1
    Does this answer your question? [How can I store objects of differing types in a C++ container?](https://stackoverflow.com/questions/4738405/how-can-i-store-objects-of-differing-types-in-a-c-container) – Alijvhr Apr 17 '23 at 10:28
  • @Alijvhr Fine. I've voted to close. – FreelanceConsultant Apr 17 '23 at 12:32
  • 1
    OP at this point if you still have a question it's going to be easier to just ask a fresh one. I apologize if this didn't help you get what you wanted. – FreelanceConsultant Apr 17 '23 at 12:35

4 Answers4

-1

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.

FreelanceConsultant
  • 13,167
  • 27
  • 115
  • 225
  • 4
    It's easy to put an object into `std::any`, but it is not easy at all to use it later. – Evg Apr 15 '23 at 17:59
  • @Evg it's trivial. Use `std::any_cast`. What use would `std::any` be if you could only put objects into it, and not retrieve data back out of it? That wouldn't make any sense. – FreelanceConsultant Apr 16 '23 at 11:53
  • 2
    Here's the problem - it's not easy to use `std::any_cast`. You'll need to specify a **type**. How would you do that if you put arbitrary objects into an array of `std::any`s in some arbitrary order? – Evg Apr 16 '23 at 13:12
  • @Evg Obviously the context will dictate what that type should be. Have you actually used `std::any` before? It's really not difficult to understand. – FreelanceConsultant Apr 16 '23 at 15:42
  • Have you understood it? Using `try-catch` blocks to catch `bad_any_cast` is almost certainly an antipattern and code smell. (Not a downvoter, btw.) – Evg Apr 16 '23 at 16:06
  • @Evg No it isn't. That is exactly the intended use. – FreelanceConsultant Apr 16 '23 at 16:21
  • @Evg How else would you "get the type back out" of a `std::any`? – FreelanceConsultant Apr 16 '23 at 16:21
  • @Evg Why do you think `std::bad_any_cast` and `std::any_cast` exist, if not for this purpose? What else do you think you would possibly use them for? – FreelanceConsultant Apr 16 '23 at 16:22
  • No, that's not the intended use. It's like using `dynamic_cast` and catching `bad_cast` to check a type of a polymorphic object. It's doable, but it's a code smell. Does that mean that `dynamic_cast` should never be used? Obviously, no, – Evg Apr 16 '23 at 16:41
  • @Evg You're completely wrong about both things. Neither of those is a code smell. You **are** supposed to attempt to `dynamic_cast` things and then handle cases where you get `nullptr`. – FreelanceConsultant Apr 16 '23 at 17:19
  • @Evg Go away and write an example of using `std::any` without using `any_cast`. You must obviously realize that saying "it can't be done" makes it obvious you have no idea what you are talking about. – FreelanceConsultant Apr 16 '23 at 17:21
  • I was not saying "it can't be done". You don't have to lie, sir. – Evg Apr 16 '23 at 17:28
  • @Evg what else am I supposed to take from you saying "this is bad don't do this" – FreelanceConsultant Apr 16 '23 at 17:46
-1

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.

Hari
  • 1,561
  • 4
  • 17
  • 26
  • OP didn't ask for a 2d object – FreelanceConsultant Apr 16 '23 at 11:32
  • 1
    As I understand it the OP wants an array that can be accessed and modified by all objects of a class. The array possibly being a "a dynamic 2d array" is also mentioned. I have updated the answer to be more broad. – Hari Apr 16 '23 at 13:46
  • This answer still doesn't seem to address the question. OP asked for a way to insert elements of arbitrary type into a container, and for that type to be variable. How does this answer address that? – FreelanceConsultant Apr 16 '23 at 15:31
  • I am not sure where the OP mentions about wanting a container that can contain elements of arbitrary type. – Hari Apr 16 '23 at 15:36
  • That is quite clearly what OP is asking. – FreelanceConsultant Apr 16 '23 at 15:42
  • Based on these sentences in the original question, it looks like a way to have a single container that can be modified by all objects of the class is needed: "Is it possible to have one array that can be used to for all objects ie all objects will be using a single array to be inputting data into." "Ive heard of something called static variable in classes [..] whether arrays can also be used like a static variable or not?" "I just need one array which can be used by all objects and not a seperate array for every object which happens when i declare an array in a class" – Hari Apr 16 '23 at 15:55
  • 1
    Ok, I now understand how you interpreted what he asked. In fairness, it isn't all that clear if he wants to put multiple types into a single container, or have a single container used by multiple instances of a single class type. I interpreted it completely differently. Hopefully OP will clarify. – FreelanceConsultant Apr 16 '23 at 16:26
-2

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!

Alijvhr
  • 1,695
  • 1
  • 3
  • 22
-3

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.

  • 1
    Hello, my name is Emory Leak. – Evg Apr 16 '23 at 13:14
  • @Evg I added the code to free the memory, I forgot. – Otávio Augusto Silva Apr 16 '23 at 19:40
  • Manual memory management is almost never a good idea in modern C++. What is an exception is thrown after `new`? Will the allocated memory be freed? Containers like `std::vector` should be preferred. – Evg Apr 16 '23 at 19:49
  • @Evg manual memory management is neither good or bad, it just adds an level of complexity for the programmer. You can use exception handling to make sure no memory goes by not freed. – Otávio Augusto Silva Apr 16 '23 at 20:12
  • And this level of complexity can easily lead to subtle bugs. That's what I mean by calling it not a good idea. In a complex program it could be very hard to track and correctly handle manual allocations, that's why modern C++ provides instruments to avoid it. – Evg Apr 16 '23 at 20:18
  • Smart pointers can be used to reduce that complexity or wrap the pointer in a class and create a proper destructor, then create static objects. But since the original question mentioned dynamic 2D arrays, I choose not to use any template containers to satisfy the rules set by the OP. – Otávio Augusto Silva Apr 16 '23 at 20:29