Roger Halliburton
Roger Halliburton

Reputation: 2021

Trying to store an object in an array but then how to call that object's methods?

I'm not a very experienced c++ coder and this has me stumped. I am passing a object (created elsewhere) to a function, I want to be able to store that object in some array and then run through the array to call a function on that object. Here is some pseudo code:

void AddObject(T& object) {
  object.action(); // this works

  T* objectList = NULL;
  // T gets allocated (not shown here) ...
  T[0] = object;
  T[0].action(); // this doesn't work
}

I know the object is passing correctly, because the first call to object.action() does what it should. But when I store object in the array, then try to invoke action() it causes a big crash.

Likely my problem is that I simply tinkered with the .'s and *'s until it compiled, T[0].action() compliles but crashes at runtime.

Upvotes: 0

Views: 1285

Answers (3)

kai26873
kai26873

Reputation: 54

The simplest answer to your question is that you must declare your container correctly and you must define an appropriate assigment operator for your class. Working as closely as possible from your example:

typedef class MyActionableClass T;
T* getGlobalPointer();

void AddInstance(T const& objInstance)
{
    T* arrayFromElsewhere = getGlobalPointer();

//ok, now at this point we have a reference to an object instance
//and a pointer which we assume is at the base of an array of T **objects**
//whose first element we don't mind losing

//**copy** the instance we've received
    arrayFromElsewhere[0] = objInstance;

//now invoke the action() method on our **copy**
    arrayFromElsewhere[0].action();

}

Note the signature change to const reference which emphasizes that we are going to copy the original object and not change it in any way.

Also note carefully that arrayFromElsewhere[0].action() is NOT the same as objInstance.action() because you have made a copy — action() is being invoked in a different context, no matter how similar.

While it is obvious you have condensed, the condensation makes the reason for doing this much less obvious — specifying, for instance, that you want to maintain an array of callback objects would make a better case for “needing” this capability. It is also a poor choice to use “T” like you did because this tends to imply template usage to most experienced C++ programmers.

The thing that is most likely causing your “unexplained” crash is that assignment operator; if you don't define one the compiler will automatically generate one that works as a bitwise copy — almost certainly not what you want if your class is anything other than a collection of simple data types (POD).

For this to work properly on a class of any complexity you will likely need to define a deep copy or use reference counting; in C++ it is almost always a poor choice to let the compiler create any of ctor, dtor, or assignment for you.

And, of course, it would be a good idea to use standard containers rather than the simple array mechanism you implied by your example. In that case you should probably also define a default ctor, a virtual dtor, and a copy ctor because of the assumptions made by containers and algorithms.

If, in fact, you do not want to create a copy of your object but want, instead, to invoke action() on the original object but from within an array, then you will need an array of pointers instead. Again working closely to your original example:

typedef class MyActionableClass T;
T** getGlobalPointer();

void AddInstance(T& objInstance)
{
    T** arrayFromElsewhere = getGlobalPointer();

//ok, now at this point we have a reference to an object instance
//and a pointer which we assume is at the base of an array of T **pointers**
//whose first element we don't mind losing

//**reference** the instance we've received by saving its address
    arrayFromElsewhere[0] = &objInstance;

//now invoke the action() method on **the original instance**
    arrayFromElsewhere[0]->action();

}

Note closely that arrayFromElsewhere is now an array of pointers to objects instead of an array of actual objects.

Note that I dropped the const modifier in this case because I don’t know if action() is a const method — with a name like that I am assuming not…

Note carefully the ampersand (address-of) operator being used in the assignment.

Note also the new syntax for invoking the action() method by using the pointer-to operator.

Finally be advised that using standard containers of pointers is fraught with memory-leak peril, but typically not nearly as dangerous as using naked arrays :-/

Upvotes: 1

Kerrek SB
Kerrek SB

Reputation: 477000

You can put the object either into a dynamic or a static array:

#include <vector> // dynamic
#include <array>  // static

void AddObject(T const & t)
{
    std::array<T, 12> arr;
    std::vector<T>      v;

    arr[0] = t;
    v.push_back(t);

    arr[0].action();
      v[0].action();
}

This doesn't really make a lot of sense, though; you would usually have defined your array somewhere else, outside the function.

Upvotes: 0

Daniel Fischer
Daniel Fischer

Reputation: 183868

I'm surprised it compiles. You declare an array, objectList of 8 pointers to T. Then you assign T[0] = object;. That's not what you want, what you want is one of

T objectList[8];
objectList[0] = object;
objectList[0].action();

or

T *objectList[8];
objectList[0] = &object;
objectList[0]->action();

Now I'm waiting for a C++ expert to explain why your code compiled, I'm really curious.

Upvotes: 0

Related Questions