6

Could somebody please explain to me why does this code only print "42" instead of "created\n42"?

#include <iostream>
#include <string>
#include <memory>

using namespace std;

class MyClass
{
public:
    MyClass() {cout<<"created"<<endl;};
    int solution() {return 42;}
    virtual ~MyClass() {};
};

int main(int argc, char *argv[])
{
    auto_ptr<MyClass> ptr;
    cout<<ptr->solution()<<endl;
    return 0;
}

BTW I tried this code with different values in solution and I always get the "right" value, so it doesn't seem to be a random lucky value.

rlazo
  • 635
  • 4
  • 14

7 Answers7

27

Because it exhibits undefined behaviour - you dereference a null pointer.

When you say:

 auto_ptr<MyClass> ptr;

you create an autopointer which doesn't point to anything. This is equivalent to saying:

MyClass * ptr = NULL;

Then when you say:

cout<<ptr->solution()<<endl;

you dereference this null pointer. Doing that is undefined in C++ - for your implementation, it appears to work.

Lazer
  • 90,700
  • 113
  • 281
  • 364
  • Oh I'm not complaining :) I upped yours after I submitted, I was slow on the draw. – GManNickG Jul 18 '09 at 19:29
  • 5
    On a serious note, I wish questioners would wait a bit before accepting an answer. Can I suggest waiting at least 12 hours? –  Jul 18 '09 at 19:32
  • 4
    I wouldn't say it's lucky -- since `solution()` isn't actually using the `this` pointer (it's not accessing any member variables or virtual functions), it's hardly surprising that it doesn't crash. – Adam Rosenfield Jul 18 '09 at 19:33
  • Well I actually changed my preferred answer to this one because it make it clear that undefined behavior means that it is up to your compiler specific c++ implementation to choose what to do, and that answered my question about why it was printing some value :). Sorry about changing my selection so often, it wont happen again. Thanks everybody for you answers, you rock! – rlazo Jul 18 '09 at 19:44
  • @Adam Actually, I was a bit (but not totally) suprised it didn't crash. But that's the wonder of UB. –  Jul 18 '09 at 19:48
21

std::auto_ptr will not automatically create an object for you. That is, ptr in main as it stands is initialized to null. Dereferencing this is undefined behavior, and you just happen to be getting lucky and getting 42 as a result.

If you actually create the the object:

int main(int argc, char *argv[])
{
    auto_ptr<MyClass> ptr(new MyClass);

    cout << ptr->solution() << endl;

    return 0;
}

You will get the output you expect.

GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • 3
    I always knew that 42 was the answer to everything, but never thought that null pointer also knew that. Thanks! BTW why this doesn't throw a segmentation fault? – rlazo Jul 18 '09 at 19:22
  • 2
    Because that's what undefined behavior does, it could appear to work, or it could reformat your computer. Probably the reason it worked is because you didn't actually operate on members of the class. The compiler sees you are accessing `MyClass::solution`. It puts in 0 for the `this` pointer, because that's what it is, goes into the function, gets 42 as a result, and returns. Give your clsas a private member `int answer`, set that to 42 in the constructor, and return that in `solution()` and you should see a crash, because now you're actually trying to use the null `this` pointer. – GManNickG Jul 18 '09 at 19:26
  • 1
    @rlazo: it doesn't throw a segfault because your solution function does not access any member variables, so the "this" pointer goes unused inside the function. – Evan Teran Jul 18 '09 at 19:27
  • 1
    It doesn't throw a segmentation fault because solution() is not virtual and doesn't access any member variables. (Not that you should count on this.) – bk1e Jul 18 '09 at 19:27
  • I think another way of putting it would be: If it could be a static function, it probably won't crash when working on a null pointer. – GManNickG Jul 18 '09 at 19:32
  • Right, I thought that because it wasn't being qualified as static it wouldn't work like that – rlazo Jul 18 '09 at 19:47
  • 1
    It 'shouldn't' :) Just a side effect of the way C++ compilers generally work. – GManNickG Jul 18 '09 at 19:52
3

First, keep in mind that the -> operator of auto_ptr is essentially forwarded on to the contained pointer. So for this discussion, your code in main becomes equivalent to:

MyClass* ptr = NULL;
cout << ptr->solution() << endl;

Then note that compilers tend to implement member functions in ways that act very much as if they were non-member functions with the this pointer passed as another function argument. So from your current compiler's point of view, your code in main acts as if it was:

MyClass* ptr = NULL;
cout << solution(ptr) << endl;

with solution written as:

int solution(MyClass* this) { return 42; }

In which case it becomes obvious why there wasn't a crash.


However as others have already mentioned, these are internal details of how compilers implement C++, which are not specified by the language standard. So in theory this code could work as described here on one compiler but crash or do something else entirely on another compiler.

But in practice, even if the standard doesn't guarantee this behavior, any particular compiler could guarantee it if they want to. For instance: since MFC relies on this behavior, it is very unlikely that Visual Studio will ever stop supporting it. Of course, you would have to research each particular compiler where your code might be used to make sure that they actually guarantee this behavior.

TheUndeadFish
  • 8,058
  • 1
  • 23
  • 17
2

Because you don't know the question to the answer xD

It seems you're not calling the constructor, right?

fortran
  • 74,053
  • 25
  • 135
  • 175
2

You a re not creating an instance of the object.
You are only creating a smart pointer.

When you call the method you are de-referencing a NULL pointer, so as Neil mentioned you are now in undefined behavior. But since your code does not try and access any member variables it luckily does not crash.

Try this:

auto_ptr<MyClass> ptr(new MyClass);
Martin York
  • 257,169
  • 86
  • 333
  • 562
1

Because ptr is uninitialized and you're lucky. You should first call new for it:

auto_ptr<MyClass> ptr( new MyClass );
Kirill V. Lyadvinsky
  • 97,037
  • 24
  • 136
  • 212
1

You're not getting a crash because the "solution" method doesn't need to actually use the class members. If you were returning a member or something, you'd probably get a crash.

Colen
  • 13,428
  • 21
  • 78
  • 107