4

The following code compiles correctly and get the mysterious output:

special Investment function 00000000

(Environment: C++ VS2010)

#include <iostream>
#include <vector>
using namespace std;

class Security {
public:
  virtual ~Security() {}
};

class Stock : public Security {};

class Investment : public Security {
public:
  void special() {
    cout << "special Investment function" << endl;
  }
};

int main() {
  Security* p = new Stock;
  dynamic_cast<Investment*>(p)->special();
  cout << dynamic_cast<Investment*>(p) << endl;
  return 0;
}

How could it be? Dereferencing a NULL pointer and get a "correct" output instead of crash? Is it a special "characteristic" of VS2010?

Now I see. I did a test and it appears that dereferencing "this" in "special" function cause the program to crash.

Thanks for your help.

Richard
  • 414
  • 6
  • 16

8 Answers8

7

Dereferencing a null pointer is undefined behavior - you can get unexpected results. See this very similar question.

In this case Investment::special() is called in a non-virtual way, so you can think the compiler just creates a global function

Investment_special_impl( Investment* this )

and calls it passing a null this pointer as the implicit parameter.

You should not rely on this.

Community
  • 1
  • 1
sharptooth
  • 167,383
  • 100
  • 513
  • 979
4

This is 'undefined bahviour'. Think of methods as having an implicit parameter, carrying 'this'. In your case, NULL was passed as actual argument for 'this'. Since you did not reference any object data refererenced (implicitly or explicitly) by 'this', it did not crash.

Had the method been virtual, it would most likely have crashed, since virtual calls are often dispatched through a lookup table associated with the object (and as a consequence 'this').

Since compiler writers are free to implement 'this' and virtual member lookup tables as they please, you shouldn't depend on this behaviour. It is undefined.

Jörgen Sigvardsson
  • 4,839
  • 3
  • 28
  • 51
2

In most implementations of C++ non-virtual methods don't require a valid instance to be called (it's faster to not check this, since standard doesn't require it).

You can set your pointer to NULL and method will still succeed if you don't access instance fields.

Virtual methods require valid vtable, so they always dereference object and cause error in case it's not initialized.

elder_george
  • 7,849
  • 24
  • 31
2

Nowhere do you dereference a null pointer in here: you simply call the function Investment::special(NULL), which is non-virtual, and in its body does not dereference this. Although the spec probably tells this is undefined behavior, it's perfectly sane from the compiler not to put any dereferencing here, so that the program does not crash.

Kristóf Marussy
  • 1,202
  • 8
  • 18
  • I think this is about 50% correct. Technically, Turner _is_ dereferencing a null pointer, because the `dynamic_cast` is illegal, which turns the pointer into a null pointer. A this-call on a null pointer is still illegal regardless whether it's virtual or not. However, it so happens that the member function does not use any class data at all, so it incidentially doesn't crash (and, possibly the compiler optimizes the this-call out, too). – Damon Jun 06 '11 at 07:56
1

Dereferencing any NULL pointer is Undefined Behaivor.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
1

You should NOT dereference the NULL pointer as it can cause Undefine Behavior. Why your code is working is because in the method:

void special() {
  cout << "special Investment function" << endl;
}

You really don't make any reference to this. To demo that, just declare a variable inside Investment class and try to print it inside special(). You will get a crash.

iammilind
  • 68,093
  • 33
  • 169
  • 336
0

Dereferencing a null pointer invokes undefined behavior, irrespective of how you get the null pointer, whether as a result of dynamic_cast or something else.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
0

I'm not sure about the dereference NULL question, but the behaviour of the code i can explain.

dynamic_cast< Investment* >(p)->special();

prints: "special Investment function"

cout << dynamic_cast < Investment* >(p) << endl;

prints: "0xABABABA" <- memory address of instance p

It's like running:

cout << p << endl;

ThePinguin
  • 31
  • 3