Say there is an object with automatic storage duration, and that object is copy-initialized from within a nested scope, like a loop body. Is the lifetime of the values created from within the nested scope extended into the containing scope?
#include<iostream>
using namespace std;
class Thing {
public:
int data;
Thing(int data) : data(data) { cout << "making a thing" << endl; }
~Thing() { cout << "destroying a thing" << endl; }
};
int main() {
Thing t = Thing(-1);
for (int i = 0; i < 4; i++) {
t = Thing(i); // this is both created AND destroyed from within this scope...?
}
cout << t.data << endl; // undefined behavior?
}
Right now, accessing t.data at the end works, but I see that the destructor for each Thing is invoked once per loop iteration, so I might just be getting lucky?
This looks relevant (but I am not a lawyer so it's tough to decipher): some 2011 c++ standard
specifically:
For such an object [with automatic storage duration] that does not have a variable length array type, its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way...
So if my code snippet is undefined behavior - for a loop body to change a local variable, that local variable should be manually heap allocated and later manually released, or...?
This is perfectly legal, defined behavior.
Your mistake is in thinking that the lifetime of the Thing created inside the block matters. It doesn't. After the copy, the copy-assigned t hasn't changed what it is; it's still the same t created outside the block, it just had its values updated. Its lifetime is entirely unaffected. In the context of your quote, the "block with which it[t] is associated" is the top-level function scope (where it was declared), not the block in which it was copy-assigned.
The Thing created inside the block does expire each time, but that's fine; it was copied from, then never used again.
There is no undefined behavior here:
#include<iostream>
class Thing {
public:
int data;
Thing(int data) : data(data) { std::cout << "making a thing" << data << std::endl; }
~Thing() { std::cout << "destroying a thing" << data << std::endl; }
Thing& operator=(const Thing &t) { data = t.data; std::cout << "operator=" << std::endl; return *this; }
};
int main() {
Thing t = Thing(-1); // create object t
for (int i = 0; i < 4; i++) {
t = Thing(i);
// 1. create an object Thing(i)
// 2. use operator= to copy that object into the object t
// 3. Thing(i) object gets destroyed at the end of scope
}
// object t is still valid, but it was modified in the loop with operator=
cout << t.data << endl; // no undefined behavior here
}
You can add copy-assignment operator (operator=) member function into the Thing class to check this. In your case this operator was implicitly added by the compiler, according to the rules in the standard:
If no user-defined copy assignment operators are provided for a class
type (struct, class, or union), the compiler will always declare one
as an inline public member of the class. This implicitly-declared copy
assignment operator has the form T& T::operator=(const T&) if all of
the following is true:
each direct base B of T has a copy assignment operator whose
parameters are B or const B& or const volatile B&
each non-static data
member M of T of class type or array of class type has a copy
assignment operator whose parameters are M or const M& or const
volatile M&
Related
I am currently reading the second edition of C++: A Beginner's Guide by Herbert Schildt.
In Module 9.4, he talks about returning objects:
Just as objects can be passed to functions, functions can return objects. To return an object, first declare
the function as returning a class type. Second, return an object of that type using the normal return
statement. The following program has a member function called mkBigger( ). It returns an object that
gives val a value twice as large as the invoking object.
This is the 'following program' he mentions:
// Returning objects.
#include <iostream>
using namespace std;
class MyClass {
int val;
public:
// Normal Constructor.
MyClass(int i) {
val = i;
cout << "Inside constructor\n";
}
~MyClass() {
cout << "Destructing\n";
}
int getval() { return val; }
// Return an object.
MyClass mkBigger() {
Myclass o(val * 2); // mkBigger() returns a MyClass object.
return o;
}
};
void display(MyClass ob)
{
cout << ob.getval() << '\n';
}
int main()
{
cout << " Before Constructing a.\n";
MyClass a;
cout << "After constructing a.\n\n";
cout << "Before call to display.\n";
display(a);
cout << "After display() returns.\n\n";
cout << "Before call to mkBigger().\n";
a = a.mkBigger();
cout << "After mkBigger() returns.\n\n";
cout << "Before second call to display.\n";
display(a);
cout << "After display() returns.\n\n";
return 0;
}
This gives us the following output:
Before Constructing a.
Inside constructor
After constructing a.
Before call to display.
10
Destructing
After display() returns.
Before call to mkBigger()
Inside constructor
Destructing
Destructing
After mkBigger() returns.
Before second call to display.
20
Destructing
After display() returns.
Destructing
Schildt then goes on to explain that the reason there are two 'Destructing' messages during the mkBigger() call is because of the fact that:
when an object is returned by a function, a temporary object is automatically created, which holds the return value. It is this object that is actually returned by the function. After the value has been returned, this object is destroyed.
I was actually surprised there wasn't 3 'Destructing' messages. I have the following issue: Given the definition of mkBigger(), a new MyClass instance is created, and it is that instance that is returned and placed in the address of a. Thus, when doing
a = a.mkBigger();
My impression is thus that the original object previously held in a is no longer referenced by a. Is this correct? If so, I then have the following issues:
I was told C++ has some minute notions of garbage collection. Would that object thus be garbage-collected? where is this object now? Is this an example of the possible feared memory leaks that many mention when talking about the 'dangers' of C++?
One of the destructor in mkbigger() is called on o, the MyClass instance passed in by value; it goes out of scope at the end of the function. The other is called on the temporary copy of o returned when it is destroyed. What else goes out of scope? Not a in main(); therefore you should not expect a third destructor to be called. C++ does not provide garbage collection outside of calling destructors when automatic objects go out of scope.
Unlike some other modern languages, a does not "hold a reference" to an object; a is the object, in that it is a certain number of bytes holding the raw data members. When you do a = a.mkBigger();, MyClass's default assignment operator is called, which simply copies the val inside the temporary object on the right hand side into the val inside a, overwriting the value that was already there. a = a.makeBigger() would be equivalent to a.val = a.makeBigger().val if val were public.
Memory leaks occur when you use new to allocate memory and then fail to use delete to deallocate that memory. For classes that do this internally, you must write at least your own copy constructor, assignment operator, and destructor.
I have some difficulties with understanding what is really done behind returning values in C++.
Let's have following code:
class MyClass {
public:
int id;
MyClass(int id) {
this->id = id;
cout << "[" << id << "] MyClass::ctor\n";
}
MyClass(const MyClass& other) {
cout << "[" << id << "] MyClass::ctor&\n";
}
~MyClass() {
cout << "[" << id << "] MyClass::dtor\n";
}
MyClass& operator=(const MyClass& r) {
cout << "[" << id << "] MyClass::operator=\n";
return *this;
}
};
MyClass foo() {
MyClass c(111);
return c;
}
MyClass& bar() {
MyClass c(222);
return c;
}
MyClass* baz() {
MyClass* c = new MyClass(333);
return c;
}
I use gcc 4.7.3.
Case 1
When I call:
MyClass c1 = foo();
cout << c1.id << endl;
The output is:
[111] MyClass::ctor
111
[111] MyClass::dtor
My understanding is that in foo object is created on the stack and then destroyed upon return statement because it's end of a scope. Returning is done by object copying (copy constructor) which is later assigned to c1 in main (assignment operator). If I'm right why there is no output from copy constructor nor assignment operator? Is this because of RVO?
Case 2
When I call:
MyClass c2 = bar();
cout << c2.id << endl;
The output is:
[222] MyClass::ctor
[222] MyClass::dtor
[4197488] MyClass::ctor&
4197488
[4197488] MyClass::dtor
What is going on here? I create variable then return it and variable is destroyed because it is end of a scope. Compiler is trying copy that variable by copy constructor but It is already destroyed and that's why I have random value? So what is actually in c2 in main?
Case 3
When I call:
MyClass* c3 = baz();
cout << c3->id << endl;
The output is:
[333] MyClass::ctor
333
This is the simplest case? I return a dynamically created pointer which lies on heap, so memmory is allocated and not automatically freed. This is the case when destructor isn't called and I have memory leak. Am I right?
Are there any other cases or things that aren't obvious and I should know to fully master returning values in C++? ;) What is a recommended way to return a object from function (if any) - any rules of thumb upon that?
May I just add that case #2 is one of the cases of undefined behavior in the C++ language, since returning a reference to a local variable is illegal. This is because a local variable has a precisely defined lifetime, and - by returning it by a reference - you're returning a reference to a variable that does not exist anymore when the function returns. Therefore, you exhibit undefined behavior and the value of the given variable is practically random. As is the result of the rest of your program, since Anything at all can happen.
Most compilers will issue a warning when you try to do something like this (either return a local variable by reference, or by address) - gcc, for example, tells me something like this :
bla.cpp:37:13: warning: reference to local variable ‘c’ returned [-Wreturn-local-addr]
You should remember, however, that the compiler is not at all required to issue any kind of warning when a statement that may exhibit undefined behavior occurs. Situations such as this one, though, must be avoided at all costs, because they're practically never right.
Case 1:
MyClass foo() {
MyClass c(111);
return c;
}
...
MyClass c1 = foo();
is a typical case when RVO can be applied. This is called copy-initialization and the assignment operator is not used since the object is created in place, unlike the situation:
MyClass c1;
c1 = foo();
where c1 is constructed, temporary c in foo() is constructed, [ copy of c is constructed ], c or copy of c is assigned to c1, [ copy of c is destructed] and c is destructed. (what exactly happens depends on whether the compiler eliminates the redundant copy of c being created or not).
Case 2:
MyClass& bar() {
MyClass c(222);
return c;
}
...
MyClass c2 = bar();
invokes undefined behavior since you are returning a reference to local (temporary) variable c ~ an object with automatic storage duration.
Case 3:
MyClass* baz() {
MyClass* c = new MyClass(333);
return c;
}
...
MyClass c2 = bar();
is the most straightforward one since you control what happens yet with a very unpleasant consequence: you are responsible for memory management, which is the reason why you should avoid dynamic allocation of this kind always when it is possible (and prefer Case 1).
1) Yes.
2) You have a random value because your copy c'tor and operator= don't copy the value of id. However, you are correct in assuming there is no relying on the value of an object after it has been deleted.
3) Yes.
I have asked this question. My question now is how this works? To elaborate, how can I point to an object that is not yet initialised. I have made this MWE and it shows that the object is copy created not copy assigned .i.e. the object is not yet initialised yet I am able to point to it.
#include <iostream>
class Foo {
public:
int x;
Foo(const Foo& ori_foo) {
std::cout << "constructor" << std::endl;
x = ori_foo.x;
}
Foo& operator = (const Foo& ori_foo) {
std::cout << "operator =" << std::endl;
x = ori_foo.x;
return *this;
}
Foo(int new_x) {
x = new_x;
}
};
class BarParent {
public:
Foo *p_foo;
BarParent(Foo* new_p_foo) : p_foo(new_p_foo)
{
std::cout << (*new_p_foo).x << std::endl;
}
};
class BarChild : public BarParent {
public:
Foo foo;
BarChild(Foo new_foo)
:BarParent(&foo) //pointer to member not yet initialised
,foo(new_foo) // order of initilization POINT OF INTEREST
{}
};
int main() {
Foo foo(101);
BarChild bar(foo);
std::cout << bar.p_foo->x << std::endl;
std::cout << bar.foo.x << std::endl;
}
Output:
constructor
0
constructor
101
101
Do not be afraid of getting into details of how the memory is handled. And, where every member resides.
Don't mistake Initialization for Allocation. BarChild::foo will be allocated before the constructor is called since it is stored in place, so there will be a well defined location for BarParent::p_foo to point at. Foo's constructor will Initialize BarChild::foo, but as long as you don't try to read from BarChild::foo before the constructor is called you will not notice the ordering.
At this line
BarChild bar(foo);
the compiler reserves enough stack space for a BarChild object then calls the constructor to begin the object's lifetime. Within the object the foo member has a fixed offset, every BarChild object has a foo member at the same offset within it, so since the this pointer has a known address within the constructor (it's the address of bar on the stack) then this->foo is also at a known address, even if the memory at that address hasn't been initialized yet. The BarChild obejct doesn't "grow bigger" as each member is initialized, its size is fixed and space for all the members is already "reserved" before they're initialized.
This is somewhat analogous to:
char raw_memory[sizeof(Foo)]; // define block of uninitialized memory
char* addr = raw_memory; // take address of uninitialized memory
new (raw_memory) Foo; // initialize memory
The object doesn't exist yet, and it's not valid to use it, but its address is known in advance of it being initialized.
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
How to return member that can be changed?
I learn that if i use in const& in the assignment (and in the called method signature) than the lifetime of the refereed object is extended until end of method.
Employee const& getEmp(int a) {
return Employee(a);
}
Employee const& tmpEmp = m.getEmp(10); //
... stuff
//end of scope - tmpEmp valid until here
I wrote little program and saw that it work as expected.
My question is not how to do this?
My question is about how compiler do this?
As you see from the example the destructor is called immediately after return , so i wonder how is that the destructor called , but tmpEmp is valid after the desructor called ?
#include <iostream>
using namespace std;
class Employee {
public:
Employee() : a(0){
cout << "c-emp" << a << endl;
}
Employee(const Employee& newE) {
a = newE.a;
cout << "c-c-emp" << a << endl;
}
Employee(int a) : a(a) {
cout << "c-emp" << a << endl;
}
~Employee() {
cout << "d-emp" << a << endl;
}
int a;
};
class Manager {
public:
Manager() {}
~Manager() {}
Employee const& getEmp(int a) {
return Employee(a);
}
};
int main(int argc, char **argv) {
Manager m;
Employee const& tmpEmp = m.getEmp(10);
cout << "tmpEmp " << tmpEmp.a <<endl;
}
output:
c-emp10
d-emp10 - destructor is called and tmpEmp is still valid? how is that?
tmpEmp 10
You learned wrong.
First, assignment never has any effect on the lifetime of an object.
It's just an operator with a side effect.
Second, if you initialize (not assign) a const reference with a
temporary, the lifetime of the temporary is extended to match the
lifetime of the reference. There are exceptions, however. (And I've
never found any pratical use for this feature.)
A const reference used as a return value is one of the exceptions (for
the simple reason that it's not implementable). Initializing a return
of a const reference type with a temporary does not extend the life of
the temporary.
And finally, even if it did, it wouldn't help you in your case, because
the reference which was initialized by the temporary ceases to exist
after the full expression which invokes the function.
Your code is wrong. You are returning a reference to a local object, and the local object is not eligible for the lifetime extension you learned about. The lifetime of local objects end before you even get a chance to assign the return value to another variable, and therefore there's no way to extend their lifetime.
The lifetime extension is for temporary objects:
Employee getEmp(int a) {
return Employee(a);
}
Employee const& tmpEmp = m.getEmp(10); //
... stuff
//end of scope - tmpEmp valid until here
Now getEmp() returns a temporary object (not a reference to a local object,) and that object is still valid when the assignment occurs. So its lifetime gets extended.
Recently, I found an interesting discussion on how to allow read-only access to private members without obfuscating the design with multiple getters, and one of the suggestions was to do it this way:
#include <iostream>
class A {
public:
A() : _ro_val(_val) {}
void doSomething(int some_val) {
_val = 10*some_val;
}
const int& _ro_val;
private:
int _val;
};
int main() {
A a_instance;
std::cout << a_instance._ro_val << std::endl;
a_instance.doSomething(13);
std::cout << a_instance._ro_val << std::endl;
}
Output:
$ ./a.out
0
130
GotW#66 clearly states that object's lifetime starts
when its constructor completes successfully and returns normally. That is, control reaches the end of the constructor body or an earlier return statement.
If so, we have no guarantee that the _val memeber will have been properly created by the time we execute _ro_val(_val). So how come the above code works? Is it undefined behaviour? Or are primitive types granted some exception to the object's lifetime?
Can anyone point me to some reference which would explain those things?
Before the constructor is called an appropriate amount of memory is reserved for the object on Freestore(if you use new) or on stack if you create object on local storage. This implies that the memory for _val is already allocated by the time you refer it in Member initializer list, Only that this memory is not properly initialized as of yet.
_ro_val(_val)
Makes the reference member _ro_val refer to the memory allocated for _val, which might actually contain anything at this point of time.
There is still an Undefined Behavior in your program because, You should explicitly initialize _val to 0(or some value,you choose)in the constructor body/Member Initializer List.The output 0 in this case is just because you are lucky it might give you some other values since _val is left unInitialized. See the behavior here on gcc 4.3.4 which demonstrates the UB.
But as for the Question, Yes indeed the behavior is Well-Defined.
The object's address does not change.
I.e. it's well-defined.
However, the technique shown is just premature optimization. You don't save programmers' time. And with modern compiler you don't save execution time or machine code size. But you do make the objects un-assignable.
Cheers & hth.,
In my opinion, it is legal (well-defined) to initialize a reference with an uninitialized object. That is legal but standard (well, the latest C++11 draft, paragraph 8.5.3.3) recommends using a valid (fully constructed) object as an initializer:
A reference shall be initialized to refer to a valid object or function.
The next sentence from the same paragraph throws a bit more light at the reference creation:
[Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” obtained by dereferencing a null pointer, which causes undefined behavior.]
I understand that reference creation means binding reference to an object obtained by dereferencing its pointer and that probably explains that the minimal prerequisite for initialization of reference of type T& is having an address of the portion of the memory reserved for the object of type T (reserved, but not yet initialized).
Accessing uninitialized object through its reference can be dangerous.
I wrote a simple test application that demonstrates reference initialization with uninitialized object and consequences of accessing that object through it:
class C
{
public:
int _n;
C() : _n(123)
{
std::cout << "C::C(): _n = " << _n << " ...and blowing up now!" << std::endl;
throw 1;
}
};
class B
{
public:
// pC1- address of the reference is the address of the object it refers
// pC2- address of the object
B(const C* pC1, const C* pC2)
{
std::cout << "B::B(): &_ro_c = " << pC1 << "\n\t&_c = " << pC2 << "\n\t&_ro_c->_n = " << pC1->_n << "\n\t&_c->_n = " << pC2->_n << std::endl;
}
};
class A
{
const C& _ro_c;
B _b;
C _c;
public:
// Initializer list: members are initialized in the order how they are
// declared in class
//
// Initializes reference to _c
//
// Fully constructs object _b; its c-tor accesses uninitialized object
// _c through its reference and its pointer (valid but dangerous!)
//
// construction of _c fails!
A() : _ro_c(_c), _b(&_ro_c, &_c), _c()
{
// never executed
std::cout << "A::A()" << std::endl;
}
};
int main()
{
try
{
A a;
}
catch(...)
{
std::cout << "Failed to create object of type A" << std::endl;
}
return 0;
}
Output:
B::B(): &_ro_c = 001EFD70
&_c = 001EFD70
&_ro_c->_n = -858993460
&_c->_n = -858993460
C::C(): _n = 123 ...and blowing up now!
Failed to create object of type A