-2
[Global Scope]

myClass *objA, *objB, *obj;
int objnum;

I want to switch between objA and objB and assign them alternatively to obj, so in main() I have:

int main()
{
  objA = new myClass(parameters...);
  objB = new myClass(parameters...);

  // start with objA;
  objnum = 0;
  obj = objA;
}

At some point a function is called that switches between the two objects:

void switchObjects()
{
  if (++objnum > 1) objnum = 0;
  obj = objnum == 0 ? objA : objB;
}

And in the function where I use the object, I have:

void doYourJob()
{
  int res = obj->work();
}

Now the weird thing is that if I don't assign obj to either objA or objB, it still works. I would expect an exception, instead. Even if I do obj = NULL;, it still works! What's this voodoo?

OK, I could provide a different example that brings to the same result, without using a NULL pointer:

myClass *obj[2];
int objnum;

void switchObject()
{
  if (++objnum > 1) objnum = 0;
}

void doYourJob()
{
  res = obj[objnum]->work();
}

int main()
{
  obj[0] = new myClass(parameters...);
  obj[1] = new myClass(parameters...);

  objnum = 0;
}

With the above code, regardless of the value of objnum, I still get both objects working together, even if I'm calling work() on only one instance.

And if I replace the function doYourJob() with this:

void doYourJob()
{
  int res1 = obj[0]->work();
  int res2 = obj[1]->work();
}

I always get the results doubled, as if I were calling the function work() twice on every object.

Mark Miles
  • 706
  • 8
  • 20

1 Answers1

4

Consider a simpler example:

#include <iostream>

struct X
{
    void foo() { std::cout << "Works" << std::endl; }
};

int main() {
    X* x = nullptr;
    x->foo();
}

With most compilers and on most platforms, this code will appear to work fine, despite having called foo on a null pointer. However, the behaviour is technically undefined. That is, the C++ language gives no restrictions about what might happen if you do this.

Why does it work? Well, calling a member function only requires knowing the type of the object it is being called on. We know that x points at an X, so we know what function to call: X::foo. In many cases, it may be difficult or even impossible to know if a pointer points at a real object, so the compiler just lets it happen. The body of the function, in this case, doesn't actually depend on the X object actually existing, so it just works. This isn't something you can depend on though.

Joseph Mansfield
  • 108,238
  • 20
  • 242
  • 324
  • I have updated my post with a different example that gives the same result. Could you please give it a look? Does it still have to do with null pointers? – Mark Miles Feb 10 '15 at 14:04
  • @MarkMiles [Can't reproduce](http://ideone.com/kI36Bj). You'll need to post a new question with a short, compilable example for anybody to help. – Joseph Mansfield Feb 10 '15 at 14:09
  • Well, I can't reproduce it either on a simpler example... My actual program is way bigger, involves many memory allocations, timers, callbacks, sockets, real-time audio, etc... The problem might be somewhere else. I tried to simplify it in this example ( http://ideone.com/bjQ1s8 ) but in my actual program whatever index I use with the class pointers I don't get any error and it still works. – Mark Miles Feb 10 '15 at 14:28
  • @MarkMiles Is there anything about the output of that program that you don't expect? As I said, if you call a member function on a pointer to an invalid object, you get undefined behaviour. It might appear to work. You shouldn't expect an error. – Joseph Mansfield Feb 10 '15 at 14:44
  • What I don't expect is that in my program if I call only `obj[0]->Work(x, y);` I get the results of both objects as if I were calling the functions of both objects. – Mark Miles Feb 10 '15 at 14:59
  • 1
    @MarkMiles Sorry, [still can't reproduce](http://ideone.com/saFyvT)! If you can't figure out how to simplify it, I can only suggest that you trace through your program or run a debugger on it. – Joseph Mansfield Feb 10 '15 at 15:02
  • I just realized that my previous example was wrong, here's a corrected version: http://ideone.com/RhYuZO – Mark Miles Feb 10 '15 at 15:03
  • @MarkMiles First, `objnum` is `0`. You call `Work` on that object. Then you call `switchObject`, so `objnum` is `1`. Then you call `Work` on that object. Then the next iteration of the for loop begins and `objnum` is *still* `1`. You call `Work` on that object.... - you're just missing a call to `switchObject`. – Joseph Mansfield Feb 10 '15 at 15:06
  • @MarkMiles By the way, don't forget to `delete` your dynamically allocated objects. – Joseph Mansfield Feb 10 '15 at 15:11