Why does this code only print 42?
Asked Answered
N

7

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.

Numidia answered 18/7, 2009 at 19:11 Comment(1)
My advice would be to consult the "Hitchhiker's Guide to the Galaxy."Hippy
R
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.

Remora answered 18/7, 2009 at 19:16 Comment(5)
Oh I'm not complaining :) I upped yours after I submitted, I was slow on the draw.Lemaster
On a serious note, I wish questioners would wait a bit before accepting an answer. Can I suggest waiting at least 12 hours?Remora
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.Rang
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!Numidia
@Adam Actually, I was a bit (but not totally) suprised it didn't crash. But that's the wonder of UB.Remora
L
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.

Lemaster answered 18/7, 2009 at 19:18 Comment(7)
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?Numidia
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.Lemaster
@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.Horror
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.)Granger
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.Lemaster
Right, I thought that because it wasn't being qualified as static it wouldn't work like thatNumidia
It 'shouldn't' :) Just a side effect of the way C++ compilers generally work.Lemaster
C
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.

Cavan answered 19/7, 2009 at 1:24 Comment(0)
S
2

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

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

Shockheaded answered 18/7, 2009 at 19:17 Comment(0)
E
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);
Edgell answered 18/7, 2009 at 19:20 Comment(1)
I would say "unluckily" rather than "luckily".Remora
D
1

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

auto_ptr<MyClass> ptr( new MyClass );
Dannadannel answered 18/7, 2009 at 19:21 Comment(0)
P
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.

Preparatory answered 18/7, 2009 at 19:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.