Sign in to follow this  
distortion

C++ subclass array access misaligned

Recommended Posts

distortion    122
I've got RTTI enabled, but it seems to be giving me trouble.
class object {
  int param1;
  int param2;
  virtual void do_stuff();
}
class sub_obj1 : public object {
  int param3;
}
class sub_obj2 : public object {
  int param4;
}
class obj_list {
  object *list;
  int num;
  template <class o_type> init(int num_objs) {
    list = new o_type[num_objs];
    num = num_objs;
  }
}

I have a particular reason for not wanting to do it as a templated class, but the trouble is, if I create an object list using init<sub_obj1>, then try and access anything in it, I am required to cast the array to (sub_obj*). This defeats the whole purpose, as these object lists are in an array with each element containing a different type. For instance if I...
obj_list mylist;
mylist.init<sub_obj1>(12);
my_list.list[6].do_stuff();

It will try and access into the array 6 * sizeof(object) instead of 6 * sizeof(sub_obj1), and it will crash trying to find the vftable in a misaligned position. On further inspection...
sizeof(my_list.list[0]) == sizeof(sub_obj1)

Evaluates to false, confirming my suspicions that the system is not taking into account the additional size of the subclass. I'm baffled. Is there some way to get it to recognize the correct size of the object?

Share this post


Link to post
Share on other sites
Antheus    2409
Quote:
sizeof(my_list.list[0]) == sizeof(sub_obj1)


sizeof is compile-time evaluated, and if evaluates to sizeof(*(object*)), or sizeof(object).

The rest is too much bad mojo for me right now to untangle, and I can hear the compiler wheeping in a dark corner.

Share this post


Link to post
Share on other sites
norisan    100
How about this? Access elements in list via index operator.

class obj_list {
object *list;
int num;
int stride;
template <class o_type> init(int num_objs) {
list = new o_type[num_objs];
num = num_objs;
stride = sizeof(o_type);
}
object& operator [] (int index)
{
return *(object*)((char*)list + index * stride);
}
}
---------
obj_list mylist;
mylist.init<sub_obj1>(12);
mylist[6].do_stuff();


Share this post


Link to post
Share on other sites
distortion    122
Quote:
Original post by norisan
How about this? Access elements in list via index operator.

*** Source Snippet Removed ***


That was the next step in my mind, though I was hoping it wasn't necessary.

Share this post


Link to post
Share on other sites
Codeka    1239
Holy crap!

Consider this:

obj_list mylist<sub_obj_1>(5);

object obj;
mylist[4] = obj;

Why do you think you can't just use std::vector<object *> (or std::vector<sub_obj2>)?

Share this post


Link to post
Share on other sites
distortion    122
Quote:
Original post by Codeka
Holy crap!

Consider this:

*** Source Snippet Removed ***
Why do you think you can't just use std::vector<object *> (or std::vector<sub_obj2>)?


The example I give is not in the context it's being used. It's merely an example of the problem being exhibited. It's in a self-contained system. Additionally, the class contains two linked lists--one for available objects and one for used objects. I want to be able to loop over all the used objects and call functionality on them without having to know what kind they are. I don't want to constantly be calling new and delete every time I want a new object, and I don't want some STL memory going ape when the number of allocated objects changes regularly and with great disparity.

Share this post


Link to post
Share on other sites
fastcall22    10846
Object splicing:


#include <iostream>
#include <iomanip>

struct object {
int param1;
int param2;
virtual void do_stuff() {
}

virtual ~object() {
}
};

struct sub_obj1 : public object {
int param3;
sub_obj1() {
param1 = 0x11111111;
param2 = 0x22222222;
param3 = 0x33333333;
}
};

struct obj_list {
object *list;
int num;

template <class o_type>
void init(int num_objs) {
delete[] list;
list = new o_type[num_objs];
num = num_objs;
}

obj_list() {
list = 0;
}

~obj_list() {
delete[] list;
}
};

int main() {
obj_list mylist;
mylist.init<sub_obj1>(4);

for ( unsigned i = 0; i < mylist.num; ++i ) {
sub_obj1& p = static_cast<sub_obj1&>( mylist.list[i] ); // sizeof(object) != sizeof(sub_obj1)
std::cout << '[' << i << "]: "
<< std::hex << std::setw( 8 ) << std::setfill( '0' ) << p.param1 << ' '
<< std::hex << std::setw( 8 ) << std::setfill( '0' ) << p.param2 << ' '
<< std::hex << std::setw( 8 ) << std::setfill( '0' ) << p.param3 << '\n';
}
}



Output:

[0]: 11111111 22222222 33333333
[1]: 011e8838 11111111 22222222
[2]: 33333333 011e8838 11111111
[3]: 22222222 33333333 011e8838




If you wanted it to work, it'd have to look something like this:

template<class o_type>
struct obj_list {
o_type *list;
int num;

void init(int num_objs) {
delete[] list;
list = new o_type[num_objs];
num = num_objs;
}

obj_list() {
list = 0;
}

~obj_list() {
delete[] list;
}
};



But that's completely redundant since std::vector does the exact same thing:


std::vector<sub_obj> objs(4); // Create four sub_objs
objs.reserve( 10000 ); // Reserve room for many sub_objs

Share this post


Link to post
Share on other sites
Zahlman    1682
Quote:
Original post by distortion
I have a particular reason for not wanting to do it as a templated class


No, you don't. Not if you're willing to make a templated member function for initialization, anyway. Seriously, you're going out of your way to avoid the natural object creation mechanism (constructors) here...

Quote:
but the trouble is, if I create an object list using init<sub_obj1>, then try and access anything in it, I am required to cast the array to (sub_obj*).


As you should be, because the pointer is not of that type.

Array indexing is equivalent to pointer arithmetic. Pointer arithmetic is based on the compile-time type of the pointer. C++ does not distinguish between pointers pointing at array-allocations and pointers pointing at scalar-allocations. (Nor those pointing at anything else, for that matter.)

Quote:
This defeats the whole purpose, as these object lists are in an array with each element containing a different type.


To make that work, you would make the list class templated, and have the template (and therefore, each instantiation) inherit polymorphically from a common non-templated base. (You could even then use std::vector for the underlying storage, and provide wrappers for e.g. operator[].)

But first, explain why you think putting these lists into the array you describe is in any way a good idea.

By the way, whereever you have virtual member functions, you almost certainly need a virtual destructor, and therefore a copy constructor and assignment operator as well. Especially if you're going to use value semantics in any way.

Quote:
I want to be able to loop over all the used objects and call functionality on them without having to know what kind they are.


Which the other proposed solution does.

Quote:
I don't want to constantly be calling new and delete every time I want a new object


So use a boost ptr_container instead, or use smart pointers.

Quote:
and I don't want some STL memory going ape when the number of allocated objects changes regularly and with great disparity.


Stop listening to the FUD and try actually learning how the standard library (not "STL") works. The element space allocated by std::vector will not exceed the required space by more than a small constant factor (typically 2) when you iteratively push_back, can be explicitly sized ahead of time, and will not resize when you remove elements. It's specifically designed to prevent memory "going ape" (i.e. being constantly re-allocated for every size-changing operation).

Share this post


Link to post
Share on other sites
nullsquared    126
Basically, an array of objects is not an array of sub_objects. Notice that we're not talking about arrays of pointers, which is when polymorphism and such would kick in and Make Things Work.

Considering your init function is templated, there is no reason your whole structure isn't. And once you fix that, you end up with a crappy version of std::vector [wink]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this