2

In my understanding that might be wrong, declaring a pointer to local object is dangerous or probably has no purpose.

The following globally defined function Factory that returns a pointer to a local object has no purpose. Right after invoking Factory(), the returned address is pointing to a local object that has been destroyed.

Foo* Factory()
{
    // It is wrong to return the address of a local object!
    // It is only for illustration!
    Foo local{ 100 };
    return &local;
}

Invoking Print() produces a garbage value. This is understandable so far.

Now consider the second case with a local scope as follows.

int main()
{
    Foo* p ;
    {
        Foo local{ 100 };
        p = &local;
    }
    p->Print();// 100 is printed even though p is pointing to a local scoped object that has been destroyed.

    return 0;
}

What I don't understand is why I can still invoke Print() on the pointed object that should have been destroyed. Any comments are welcome!

Complete Example

#include <iostream>

class Foo
{
private:
    int _data;
public:
    Foo(int data) :_data{ data }
    {
        std::cout << "Ctor: " << this << std::endl;
    }
    ~Foo()
    {
        std::cout << "Dtor: " << this << std::endl;
    }
    void Print()
    {
        std::cout << "Data: " << _data << std::endl;
    }
};


Foo* Factory()
{
    // It is wrong to return the address of a local object!
    // It is only for illustration!
    Foo local{ 100 };
    return &local;
}

int main_1()
{
    Foo* p = Factory();
    p->Print();// Garbage gets printed because p is pointing to a local object that has been destroyed.

    return 0;
}

int main_2()
{
    Foo* p ;
    {
        Foo local{ 100 };
        p = &local;
    }
    p->Print();// 100 is printed even though p is pointing to a local scoped object that has been destroyed.

    return 0;
}
Second Person Shooter
  • 14,188
  • 21
  • 90
  • 165
  • May I ask what is with this line: `Foo(int data) : _data{data} { }`. Don't initializer lists look like `Foo(int data) : _data(data) { }`? – rrswa Mar 01 '21 at 05:24
  • 9
    Both examples invoke [undefned behavior](https://en.cppreference.com/w/cpp/language/ub). That means anything is allowed to happen from that point on, but it does not mean that either code has to necessarily crash or print garbage values. – dxiv Mar 01 '21 at 05:25
  • @dxiv: If it is the answer, please submit it as your answer. I will accept it. Otherwise, I will delete my question. – Second Person Shooter Mar 01 '21 at 05:29
  • @Ruan: I use uniform initializer syntax in modern c++. – Second Person Shooter Mar 01 '21 at 05:30
  • Possibly dupe: https://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope/6445794#6445794 – Tas Mar 01 '21 at 05:30
  • @Tas: Yes only the first case is a duplicate. – Second Person Shooter Mar 01 '21 at 05:31
  • @MoneyOrientedProgrammer Similar questions have been asked before, so I expect yours to be closed as a duplicate eventually. There is even a meta-question about it [here](https://stackoverflow.com/questions/2235457/how-to-explain-undefined-behavior-to-know-it-all-newbies) with some entertaining answers. – dxiv Mar 01 '21 at 05:42
  • @dxiv: May I ask another relevant question? Is returning an address of locally created object in heap memory such as in `Foo* Factory(){ return new Foo{ 100 };}` undefined behavior? – Second Person Shooter Mar 01 '21 at 05:52
  • 1
    No. Something, which is allocated by `new` has a life-time until `delete` is called for the address returned by `new` (or end of the process). It's regardless where `new` and `delete` are placed (concerning this). – Scheff's Cat Mar 01 '21 at 06:32
  • 1
    @MoneyOrientedProgrammer Objects created on the heap live until deleted, so the `Factory` pattern is perfectly safe (and, in fact, fairly common). – dxiv Mar 01 '21 at 06:52

1 Answers1

2

Undefined behavior

First I want to give you a brief explanation of undefined behavior.
If you want to you can read more here Undefined behavior.

Undefinded behavior renders the entire program meaningless if certain rules of the language are violated. The compiler can basicly decide what to do.

But like mentioned on cppreference:
Compilers are not required to diagnose undefined behavior (although many simple situations are diagnosed), and the compiled program is not required to do anything meaningful. So in short there are no restrictions on the behavior of the program.


Your case

Both examples invoke undefned behavior:

int main_2()
{
    Foo* p ;
    {
    Foo local{ 100 };
    p = &local;
    }
    p->Print();
    return 0;
}

int main_1()
{
    Foo* p = Factory();
    p->Print();

    return 0;
}

So like we learned undefinded behavior is undefinded. So anything can happen. In the second case your compiler was probably smart enough to notice the issue.

Because the behavior is undefined your program could also crash or do other weird things.

Ferdi Kedef
  • 126
  • 1
  • 3