tags:

views:

633

answers:

7

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.

+26  A: 

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.

anon
That was an interesting trade of accepted answer, haha. :P
GMan
But mine was first :-)
anon
Oh I'm not complaining :) I upped yours after I submitted, I was slow on the draw.
GMan
On a serious note, I wish questioners would wait a bit before accepting an answer. Can I suggest waiting at least 12 hours?
anon
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
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
@Adam Actually, I was a bit (but not totally) suprised it didn't crash. But that's the wonder of UB.
anon
@rlazo Wait a bit before accepting an answer, but by all means change your acceptance if a better one comes along.
anon
+2  A: 

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

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

fortran
That's what I was thinking...
CalebHC
+20  A: 

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.

GMan
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
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.
GMan
@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
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
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.
GMan
Right, I thought that because it wasn't being qualified as static it wouldn't work like that
rlazo
It 'shouldn't' :) Just a side effect of the way C++ compilers generally work.
GMan
+2  A: 

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
I would say "unluckily" rather than "luckily".
anon
+1  A: 

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

auto_ptr<MyClass> ptr( new MyClass );
Kirill V. Lyadvinsky
+1  A: 

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
+3  A: 

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