Dismiss Notice
Join Physics Forums Today!
The friendliest, high quality science and math community on the planet! Everyone who loves science is here!

C/++/# Arrays/vectors without knowing type

  1. Jun 19, 2017 #1

    ChrisVer

    User Avatar
    Gold Member

    Hi,

    Is it possible to declare an array list or vector in C++ without knowing beforehand the type of objects it may contain? (in fact it may contain any kind of object)
    For example if I have the following classes (maybe there are some mistakes overall, but my main concern is how to deal with the vector TYPE which should either be an Orange or an Apple):
    Code (C):

    class Fruits{
       private:
          int m_Nfruit=0;
          //HERE NEED HELP
          vector<TYPE??> fruitContainer;
       public:
          Fruits() { m_Nfruits += 1; };
          virtual ~Fruits() { };
          void Print() const {
                for( std::vector<TYPE>::iterator it = fruitContainer.begin() ; it!=fruitContainer.end() ; it++){
                     it->Print() // should print either Apple or Orange
                }
          };
    };

    class Orange : public Fruits{
        private:
           int m_Norange = 0;
        public:
           Orange(){
                 Fruit::Fruits()
                 m_Norange +=1 ;
                 fruitContainer.push_back(*this)
                 };
           virtual ~Orange(){};
           void Print() const {
               std::cout << "Orange" << std::endl;
           };
    }

    class Apple : public Fruits{
        private:
           int m_Napple = 0;
        public:
           Apple(){
                 Fruit::Fruits()
                 m_Napple +=1 ;
                 fruitContainer.push_back(*this);
                 };
           virtual ~Apple(){};
           void Print() const {
               std::cout << "Apple" << std::endl;
           };
    }
     
     
  2. jcsd
  3. Jun 19, 2017 #2

    Mark44

    Staff: Mentor

    No, but with polymorphism, you can create a vector of some base class (such as Fruits in your example), and have it contain objects of various derived classes.
    What you have below looks like a step in the right direction.
     
  4. Jun 20, 2017 #3

    ChrisVer

    User Avatar
    Gold Member

    The problem with the vector is that it should know what it contains though... like int, float, double etc , and in this case oranges or apples...
    I thought about moving the container as a member of the derived classes (haven't tried that out yet). So have vector<Orange> or vector<Apple>... and using the same name convention it could be used in the Print function of the Base class.
     
  5. Jun 20, 2017 #4

    Mark44

    Staff: Mentor

    With polymorphism, if you declare the vector in the base class (Fruits), all the vector "knows" is that it will contain objects of the derived classes -- Apple or Orange objects in your example code. The vector won't "care" about the types of the objects it contains, but your code can determine which type of fruit a particular object is by calling the object's Print() method. In the base class, you could make the Print() method virtual, and make sure that any derived class provides an implementation of this method.
    This is a different kind of solution, using a templatized vector. The Standard Template Library (STL) vector type you are using is already defined in terms of templates (as in vector<int>). You can extend this idea to create a vector whose base type is Apple or Orange, whatever, but the difference is that each vector will contain only one object type. For more info, see https://msdn.microsoft.com/en-us/library/y097fkab.aspx.
     
  6. Jun 20, 2017 #5

    Vanadium 50

    User Avatar
    Staff Emeritus
    Science Advisor
    Education Advisor

    The answer to the question you asked is that it is possible to do this with an array of void pointers. However, as pointed out, this is almost certainly not what you want.
     
  7. Jun 20, 2017 #6

    chiro

    User Avatar
    Science Advisor

  8. Jun 21, 2017 #7
    @Mark44 You should absolutely never use polymorphism in a vector or any other template class.

    Here is why:
    Code (Text):
    struct A {
         int m_a;
         int m_b;
         int m_c;
    };

    struct B : public A {
         int m_d;
    };

    std::vector<A> items;
    The problem should be obvious. When you push something onto the vector, it'll create a new instance of that object in the vector's internal array. You told the compiler that the vector contains objects of type A, so it will allocate enough space to hold an A. In the example above, A is 3 byte, and B is 4. Your compiler may let you do it, (GCC does) but the results will be undefined so never ever do it, it will work only in the case where the two classes are the exact same size. The vtable may also get screwed up as it will create the new internal copy with A's constructor.


    If you use polymorphism you have to use a vector of pointers.
    Code (Text):
    std::vector<A *> items;
    Now you can put whatever you want in it because the vector only allocates the space for a new pointer, not a new object. Of course, then you have to be sure to clean up all of the memory yourself:
    Code (Text):
    std::vector<A *>::iterator i = items.begin();
    std::vector<A *>::iterator e = items.end();
    for(; i != e; ++i){
         delete (*i);
    }



    If you want a truly type agnostic vector ala PHP, you'll have to wrap your item in a universal wrapper.
     
  9. Jun 21, 2017 #8

    Mark44

    Staff: Mentor

    I don't see the problem, at least based on your example. The items variable, of type vector<A>, is intended to hold instances of the A object. I wouldn't expect someone to push a B object onto this vector. In any case, your example is more about templates than it is about polymorphism.

    In the OP's example, with a Fruits base class, with Apple and Orange classes derived from Fruit. all of the class instances take up the same amount of space in memory: 4 bytes for the private member. If he were to create a vector of Fruits objects, the vector could contain Orange or Apple instances, I believe. I'm using VS, which stores only the member data in a struct or class instance, so at least in this implementation, instances of these three types take up the same amount of memory. The Borland compiler I had years ago also stored the vtable, with pointers to the member functions, but the VS C++ compiler doesn't seem to do this.

    BTW, A contains 3 ints, at 4 bytes each, for a total of 12 bytes, and B contains 4 ints, for a total of 16 bytes.
     
  10. Jun 21, 2017 #9
    Yes, but you are forgetting about the vtable.

    In the case of Fruit, if you give an object of type Apple to it. It will call the copy constructor for Fruit. So anything that's not initialized by the Fruit(const Fruit &) function will end up being junk. That's not just missing variables but also vtable references. If you call Print() from any object in that vector, it will call Fruit::Print.
     
  11. Jun 21, 2017 #10

    Mark44

    Staff: Mentor

    I suggested earlier that the Print() method on the Fruit class should be virtual, with derived classes implementing this method. Since a Fruit object is not useful, it should probably be defined as an abstract class.

    Anyway, I haven't given any real thought to this stuff for about 20 years, but I seem to remember writing some code with an array of some base type into which you could insert objects of the derived types. I'll have to do some more research.
     
    Last edited: Jun 21, 2017
  12. Jun 21, 2017 #11

    ChrisVer

    User Avatar
    Gold Member

    So if I understand well, if you tell it to expect addresses (pointers) than objects, you can do the job and add elements of the derived classes?
    i.e. (not in the class yet but in the code):
    Code (C):

    vector<*Fruit> v;
    Orange * orange = new Orange()
    Apple* apple = new Apple()
    v.push_back(orange);
    v.push_back(apple);
    for( auto it = v.begin(); it != v.end() ; it++){
       //it is a pointer to the pointer of apple or orange, so have to dereference to get the pointer to the actual object
       (*it)->Print(); //prints implementation in Apple/Orange class
    }
     
     
  13. Jun 21, 2017 #12
    Yes, that is correct. Just remember that when you create something with new, you have to explicitly call delete when you are done with it.
     
  14. Jun 22, 2017 #13
    Do not store raw owning pointers in a vector. This obsolete, dangerous code with new and delete will bite you. As other answers have said, when the container goes out of scope, the pointers will be deleted but not the objects they point to. What other answers have not said is that naive manual cleanup of the vector is insufficient because that code will not be executed if an exception occurs. The proper solution is to use a vector of std::unique_ptr.

    As for your question, consider using the recently added std::variant or the well-established boost::variant. This topic is anything but new and several solutions exist. If you want to research it further, the search term you are looking for is heterogeneous container.
     
Know someone interested in this topic? Share this thread via Reddit, Google+, Twitter, or Facebook

Have something to add?
Draft saved Draft deleted