0

I don't understand why this code works:

class Base {
public:
    virtual void doStuff() const{}
    Base(Base & b) = delete;
    Base() {
        cout << "Base" << endl;
    }
    ~Base() {
        cout << "~Base" << endl;
    }
    friend ostream& operator<<(ostream& out, const Base& a) {
         return out << "me";
    }
};
int main(){
    unique_ptr<Base> pt = make_unique<Base>();
    auto npt = move(pt);
    auto &a = *pt;

    if (pt == nullptr)
        cout << "yes nullptr" << endl;
    cout << a << endl;
}

The output in Visual Studio 2015 is:

Base
yes nullptr
me
~Base

So it doesn't crash and pt is even usable after being moved from. In coliru online compiler, it crashes at line cout << a << endl;. I don't understand how it doesn't crash at line auto &a = *pt;, since pt is equal to nullptr at this point and the command auto &refToNull= nullptr; is a compilation error.

I will appreciate a clarification about what's going on.

darkThoughts
  • 403
  • 6
  • 18
  • 4
    Undefined behavior is undefined. – songyuanyao Jul 30 '17 at 14:12
  • You forget that your `operator<<` function is *not a member function*. It's a global non-member function. Try calling an actual member function, or accessing a member variable instead. – Some programmer dude Jul 30 '17 at 14:13
  • 2
    _So it doesn't crush and `pt` is even usable after being moved from._ Who says that it should always crash? It is Undefined behavior. – Algirdas Preidžius Jul 30 '17 at 14:13
  • 1
    Your example boils down to `Base& a = *(Base*)nullptr; cout << a;` That exhibits UB in the same way, and I predict would produce the same outcome. `unique_ptr` or `move` have nothing to do with it - they just form a very elaborate way to spell `(Base*)nullptr`. – Igor Tandetnik Jul 30 '17 at 14:17

2 Answers2

1

First of all, the statement

auto &a = *pt;

is an Undefined Behaviour, that is not defined. Dereferencing a nullptr in C++ does not make crash your program, it may happen everything.


What you probably expect from your code is a segmentation fault, but it does not occur because you actually does not ever access to the the object a.

Indeed, your operator<< take a Base object but it does not use it at all.

Instead, if you try to do something like:

friend ostream& operator<<(ostream& out, const Base& a) {
     a.do_stuff();
}

Your program will be killed by the OS because a object is referenced on a wrong memory (actually 0x0 memory).

A related question about nullptr derefereing is Here.

BiagioF
  • 9,368
  • 2
  • 26
  • 50
0

auto npt = move(pt); transfers ownership of pt to npt leaving pt a nullptr. Since pt is now a nullptr then if (pt == nullptr) cout << "yes nullptr" << endl; outputs "yes nullptr" correctly.

auto &a = *pt; means you are trying to dereference a nullptr this is simply an undefined behavior. cout << a << endl; can lead to anything can happen but not necessarily crashing program.

duong_dajgja
  • 4,196
  • 1
  • 38
  • 65