-3

So here are some supposed facts I've read and know.

1) Objects made (IGNORING NEW/DYNAMIC MEMORY) inside functions cannot be return (they disappear after functions are done)

Example:

std::string* getTestString()
{
    std::string s = "STRING MADE IN FUNCTION";
    std::cout << "(INSIDE FUNCTION) DEREFERENCE (ADDRESS OF STRING): " << *(&s) << std::endl;
    std::cout << "(INSIDE FUNCTION) RETURN (ADDRESS OF STRING): " << &s << std::endl;
    return &s;
}
//main function
{
    std::string* s = getTestString();
    std::cout << "(IN MAIN) STRING POINTER: " << s << std::endl;
    std::cout << "(IN MAIN) ADDRESS OF (STRING POINTER): " << &s << std::endl;
    std::cout << "(IN MAIN) DEREFERENCE OF (STRING POINTER): " << *s << std::endl;
}

The result from console:

(INSIDE FUNCTION) DEREFERENCE (ADDRESS OF STRING): STRING MADE IN FUNCTION
(INSIDE FUNCTION) RETURN (ADDRESS OF STRING): 0x7fff85791040
(IN MAIN) STRING POINTER: 0x7fff85791040
(IN MAIN) ADDRESS OF (STRING POINTER): 0x7fff85791088
The program has unexpectedly finished.

TO SUMMARIZE:

1) String s made inside function

2) ADDRESS of String s returned

3) Since the function is DONE, String s and stuff is GONE

4) In main, the ADDRESS of String s is returned, but what it is (used to) pointing to is GONE

5) You try to dereference that address and since the stuff is GONE, it crashes

This makes sense.

2) This example:

class TestObject
{
public:
    TestObject();
    ~TestObject();
    int getTestVariable();

private:
    int testVariable = 9999;
};

//IN MAIN.CPP

TestObject testTestObject1()
{
    TestObject testObject;
    std::cout<<"(INSIDE FUNCTION) ADDRESS OF TESTOBJECT: "<<&testObject<<std::endl;
    std::cout<<"(INSIDE FUNCTION) TESTOBJECT.VARIABLE: "<<testObject.getTestVariable()<<std::endl;
    return testObject;
}

TestObject* testTestObject2()
{
    TestObject testObject;
    std::cout<<"(INSIDE FUNCTION) RETURN (ADDRESS OF TESTOBJECT): "<<&testObject<<std::endl;
    std::cout<<"(INSIDE FUNCTION) TESTOBJECT.VARIABLE: "<<testObject.getTestVariable()<<std::endl;
    return &testObject;
}

{
    TestObject tObject1 = testTestObject1();
    std::cout << tObject1.getTestVariable() << std::endl;
    std::cout << "(IN MAIN) ADDRESS OF (TESTOBJECT): " << &tObject1 << std::endl;

    TestObject* tObject2 = testTestObject2();
    std::cout << tObject2->getTestVariable() << std::endl;
    std::cout << "(IN MAIN) TESTOBJECT POINTER: " << tObject2 << std::endl;
}

The result in console:

(INSIDE FUNCTION) ADDRESS OF TESTOBJECT: 0x7fff1aa91520
(INSIDE FUNCTION) TESTOBJECT.VARIABLE: 9999
9999
(IN MAIN) ADDRESS OF (TESTOBJECT): 0x7fff1aa91520

(INSIDE FUNCTION) RETURN (ADDRESS OF TESTOBJECT): 0x7fff1aa914e0
(INSIDE FUNCTION) TESTOBJECT.VARIABLE: 9999
9999
(IN MAIN) TESTOBJECT POINTER: 0x7fff1aa914e0

TO SUMMARIZE:

Regardless of what we just found out in example #1, this example works! WTF BS! What StackOverflow said is that #1 is correct and why you should use new/dyanmic memory to return pointers, but #2 works, why?

1) testTestObject1() makes a TestObject inside function

2) then returns by reference(??) it should be by value but if you look, the addresses are the same? why?

3) testTestObject2() makes a TestObject inside function

4) then returns the address as a pointer

5) but the variable and object still exists even though it should be gone because the function is GONE

IN SHORT:

Can someone explain to me why objects that were made in functions still exists even after the functions are finished?

Why should C++ programmers minimize use of 'new'?

When to use "new" and when not to, in C++?

When should I use the new keyword in C++?

Community
  • 1
  • 1
T t
  • 19
  • 1
  • 1
  • Compilers are allowed to optimize code, and expecting consistent results from undefined behaviour is a bad idea. [Please read this](http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html). – chris Apr 12 '15 at 00:49
  • 2
    Too long and difficult to read. You can assume we understand that returning the address of local variables is bad etc and get right to your question. – Neil Kirk Apr 12 '15 at 00:49
  • One possible consequence of invoking undefined behavior is that the operation works anyway. Nevertheless, such coding practices are to be avoided. – Jim Lewis Apr 12 '15 at 00:56
  • "But daddy can't be dead — his watch is still right there on his arm." – molbdnilo Apr 12 '15 at 00:57

3 Answers3

2

Your second case is as bad as the first one, you return the address of a stack locally-constructed object. Any decent compiler should emit a warning in that case, with proper warning level (use -Wall -Wextra in g++ to make sure you catch these kind of things).

Example on my system (aside from linker errors due to incomplete definitions, and even without warning flags):

line 24: warning: address of local variable 'testObject' returned

and live here http://ideone.com/mc1ueG

If your program compiles doesn't mean your program "works". It just gives rise to undefined behaviour (UB), which is the most perverted error you can get in a C/C++ program, as sometime it is really hard to diagnose. Why UB is used in C/C++? Because you don't want to pay for what you don't use, and the compiler just doesn't bother enforcing strict checking on some statements (although most of the time emits warnings). Such verifications would take time, and again, you don't want them automatically enforced.

vsoftco
  • 52,188
  • 7
  • 109
  • 221
1

Your simple example works because your code is simple - the stack memory your objects used is still there, and because you haven't returned from main() nothing has modified it. Your local object even has exactly the same data in exactly the same place in your successive calls to your class methods.

std::string isn't a simple object. On any OS/C++ runtime it's quite likely to do a lot of dynamic memory allocation such that if you refer to the object after its destructor is called your process will fail.

As others have already pointed you - you're acting surprised something undefined works differently under different circumstances.

Andrew Henle
  • 27,654
  • 3
  • 23
  • 49
0

As said before you are lucky for not getting undefined behaviour because your memory is not used by other program, but what will happen if we force the program to use the pointer that you return from your function using new operator placement that force allocation of memory at certain pointer:

TestObject* testTestObject2()
{
/*static*/   TestObject testObject;
  std::cout<<"(INSIDE FUNCTION) RETURN (ADDRESS OF TESTOBJECT):"<<&testObject<<std::endl;
  std::cout<<"(INSIDE FUNCTION) TESTOBJECT.VARIABLE: "<<testObject.getTestVariable()<<std::endl;
  return &testObject;
}

 TestObject* tObject2 = testTestObject2();

 TestObject * tObject3 = new  (tObject2) TestObject;

 std::cout << tObject2->getTestVariable() << std::endl;

output:

---> here the compiler dosn't protect your memory "Undifined behaviour" because I placed the data in the same pointer using new:

(IN MAIN) ADDRESS OF (TESTOBJECT): 0x7ffffddcb0b8
(INSIDE FUNCTION) RETURN (ADDRESS OF TESTOBJECT): 0x7ffffddcafe8
(INSIDE FUNCTION) TESTOBJECT.VARIABLE: 9999
-35868696  (undefined behavior)
(IN MAIN) TESTOBJECT POINTER: 0x7ffffddcafe8

lets see if we protect your return pointer by static keyword:

TestObject* testTestObject2()
{
static   TestObject testObject;
  std::cout<<"(INSIDE FUNCTION) RETURN (ADDRESS OF TESTOBJECT): "<<&testObject<<std::endl;
  std::cout<<"(INSIDE FUNCTION) TESTOBJECT.VARIABLE: "<<testObject.getTestVariable()<<std::endl;
  return &testObject;
}

output: here the programe use a different address to store the data from return value

(IN MAIN) ADDRESS OF (TESTOBJECT): 0x7fff479bd8c8
(INSIDE FUNCTION) RETURN (ADDRESS OF TESTOBJECT): 0x6021dc
(INSIDE FUNCTION) TESTOBJECT.VARIABLE: 9999
9999  (data is intact)
(IN MAIN) TESTOBJECT POINTER: 0x6021dc
youssef
  • 605
  • 3
  • 14