#include <iostream>
class Base
{
public:
virtual int Do(int) = 0;
};
class Work :public Base
{
public:
int Do(int v) override
{
return v;
}
int s = 100; //If move the 's' to class Base, the second method is succeed.
};
int main()
{
std::cout << "first:\n";
Base* w1 = new Work;
std::cout << w1->Do(7) << "\n";
delete w1;
std::cout << "second:\n";
Base* w2 = new Work[10];
for (int i = 0; i < 10; i++)
{
std::cout << w2[i].Do(5) << "\n"; //Program is crash when array index = 1
}
delete[]w2;
}
I’m a bit rusty on poor old C-style arrays. Haven’t used them in more than 10 years and I suggest you do that as well
With that out of the way, here is what I think is happening:
Base* w2 = new Work[10];
This is creating a Work array with 10 objects and is storing the address of the first element in w2 of type Base*. So far so good…
Now the way w2[i] works is it gets the address of the first element (so w2) and adds i*sizeof(typeof(w2)). Now the problem is that typeof(w2) returns Base* and Base* size is smaller than the size of the actual stored object Work* since Base* doesn’t contain the int s and Work* does. This makes w2[1] to return an address somewhere in the middle of the first object instead of the beginning of the second one. To avoid this you have to set your array of type Work* or move the int s in class Base{...}. This will allow the operator[i] to properly increment the address.
Now this is a quirk of C-style arrays as they were never designed to work for objects and inheritance. This is the reason they are only used in very, very low level code usually to store fundamental types.
In Unreal use TArray<Base*> and in pure C++ use std::vector<Base*>.
P.S.
A fun thing you can try - print out sizeof(Base*) and sizeof(Work*). I suspect that the Work* size is exactly two time the size of Base*. In that case you can try w2[i*2].Do(5) and it might work. Never use that in real code though - it creates so many opportunities for confusion and errors.