-1

I have to make a dynamic array in a class and store another class' object in that array. Then use the array to access the other classes. there are a total of 3 classes to be stored in that same array.

But when I try to access the var, it wont allow me to... (obj.arr[0].a;) This one. I tried many ways but cant find any solution. please help.

Code:

class T1
{
public:
    int a = 1;
};

class T2
{
public:
    int a = 2;
};


class T
{
public:
    void *arr[];
};

int main()
{

    T obj;

    obj.arr[0] = new T1;
    obj.arr[1] = new T2;

    obj.arr[0].a;
}
Yksisarvinen
  • 18,008
  • 2
  • 24
  • 52
  • 5
    Don't use `void*`'s. Once you do that conversion, you don't know what it pointed to anymore. Looks like you might need a `std::variant` or `std::any`. – NathanOliver Jan 25 '21 at 17:03
  • I have to make the array inside a custom-built class. I can not use system-built classes like std::variant or std::any your response was much appriciated. – muhammad omer irfan Jan 25 '21 at 17:12
  • 1
    *"I can not use system-built classes like..."* - because ? I mean, *I* can't because my org isn't yet up to using a C++17 compliant toolchain, and doesn't allow using `boost`. but, out of curiosity, why can't you ? – WhozCraig Jan 25 '21 at 17:14
  • 1
    @muhammadomerirfan In that case, you'll need to build your own tagged union (basically what `std::variant` is). – NathanOliver Jan 25 '21 at 17:24

2 Answers2

1

It does not want to let you, because a void* has no member named a. You have to let the compiler know that you want to pretend the void* is actually a T1*.

That being said, this is a terrible practice. One of the advantages of c++ over c is that it provides a way of avoiding the need to force-polymorphism like this. A much better way would be to make all classes inherit a common base class (and instead of void pointers, use pointers to that base class in your array). If virtual methods satisfy your needs, use those, otherwise use dynamic_cast (https://en.cppreference.com/w/cpp/language/dynamic_cast) to retrieve the whole objects.

If you still insist on retrieving the object from the void*, do so using static_cast, and triple-check that you have actually put the object under that pointer.

static_cast<T1*>(&obj.arr[0])->a;

Your void*[]'s size is also not initialized, but I assume that is only for simplicity?

IWonderWhatThisAPIDoes
  • 1,000
  • 1
  • 4
  • 14
  • 1
    [Should I use static_cast or reinterpret_cast when casting a void* to whatever](https://stackoverflow.com/questions/310451/) – Remy Lebeau Jan 25 '21 at 17:21
0

To do what you are attempting, you would need an explicit type-cast of each array element, eg:

class T1
{
public:
    int a = 1;
};

class T2
{
public:
    int a = 2;
};


class T
{
public:
    void **arr;
};

int main()
{
    T obj;

    obj.arr = new void*[2];
    obj.arr[0] = new T1;
    obj.arr[1] = new T2;

    static_cast<T1*>(obj.arr[0])->a;
    static_cast<T2*>(obj.arr[1])->a;

    ...

    delete static_cast<T1*>(obj.arr[0]);
    delete static_cast<T2*> obj.arr[1];
    delete[] obj.arr;
}

However, that begs the question - how do you know which type to cast each element to? You could do something like this:

enum Ttype {typT1, typT2};

class TBase
{
    Ttype type;
    TBase(Ttype type) : type(type) {}
    virtual ~TBase() {}
};

class T1 : public TBase
{
public:
    int a = 1;
    T1() : TBase(typT1) {}
};

class T2 : public TBase
{
public:
    int a = 2;
    T2() : TBase(typT2) {}
};


class T
{
public:
    void **arr;
};

int main()
{
    T obj;

    obj.arr = new void*[2];
    obj.arr[0] = static_cast<TBase*>(new T1);
    obj.arr[1] = static_cast<TBase*>(new T2);

    for(int i = 0; i < 2; ++i) {
        TBase *tb = static_cast<TBase*>(obj.arr[i]);
        switch (tb->type) {
            case typT1:
                static_cast<T1*>(tb)->a;
                break;
            case typT2:
                static_cast<T2*>(tb)->a;
                break;
        }
    }

    ...

    delete static_cast<TBase*>(obj.arr[0]);
    delete static_cast<TBase*>(obj.arr[1]);
    delete[] obj.arr;
}

But that is ugly, and gets tedious to maintain over time as more classes are added.

You should move the common a member into the base class, and get rid of the void* altogether. No need for casts, let polymorphism work for you:

class TBase
{
    int a;
    TBase(int a) : a(a) {}
    virtual ~TBase() {}
};

class T1 : public TBase
{
public:
    T1() : TBase(1) {}
};

class T2 : public TBase
{
public:
    T2() : TBase(2) {}
};


class T
{
public:
    TBase **arr;
};

int main()
{
    T obj;

    obj.arr = new TBase*[2];
    obj.arr[0] = new T1;
    obj.arr[1] = new T2;

    for(int i = 0; i < 2; ++i) {
        obj.arr[i]->a;
    }

    ...

    delete obj.arr[0];
    delete obj.arr[1];
    delete[] obj.arr;
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770