Object lifetime is usually tied to scope but when an object is created inside a function call things become a bit blurrier (at least to me). Check the code below:
#include <iostream>
#include <string>
using namespace std;
struct ins
{
ins() : _num(++num) { cout << "ctor " << _num << endl; }
~ins() { cout << "dtor " << _num << endl; }
int get() { return _num; }
static int num;
int _num;
};
int ins::num = 0;
ins geti() { return {}; }
void usei(int i) { cout << "using " << i << endl; }
int main()
{
usei(geti().get());
cout << endl;
usei(geti().get());
}
Output
ctor 1
using 1
dtor 1
ctor 2
using 2
dtor 2
Demo
Long story short, I want to know if there's a way to "calculate" the lifetime of objects in such cases. How would I know to what function each object is tied to? my current mental model of what's going on does not help me much.
After digging into cppreference, I found a section on object lifetime. Apparently my mental model on this was wrong.
Lifetime does not bind to functions in this case, but to expressions. My case pertains to temporary object lifetime, which is created by
returning a prvalue from a function
In that case the following rule applies:
All temporary objects are destroyed as the last step in evaluating the
full-expression that (lexically) contains the point where they were
created, and if multiple temporary objects were created, they are
destroyed in the order opposite to the order of creation. This is true
even if that evaluation ends in throwing an exception.
Related
Take a look at this code:
#include <iostream>
using namespace std;
class A {
private:
int _x;
int _id;
static int count;
public:
A(int x) : _x(x) {
this->_id = A::count++;
cout << "Object with id " << this->_id
<< " has been created." << endl;
}
~A() {
cout << "Object with id " << this->_id
<< " has been destroyed." << endl;
}
int get_x(void) {
return this->_x;
}
A add(A& object) {
A tmp(this->_x + object._x);
return tmp;
}
};
int A::count = 1;
int main(void) {
A object_1(13);
A object_2(5);
A object_3(12);
object_3 = object_1.add(object_2);
cout << object_3.get_x() << endl;
return 0;
}
Here's the output from the program:
Object with id 1 has been created.
Object with id 2 has been created.
Object with id 3 has been created.
Object with id 4 has been created.
Object with id 4 has been destroyed.
18
Object with id 4 has been destroyed.
Object with id 2 has been destroyed.
Object with id 1 has been destroyed.
I don't understand what happened to Object with id 3? It definitely was created, but I see no line telling me that it was ever destroyed. Can you please tell me what's going on here?
As an aside question, why is it that when I use return 0, the destructors work fine, but when I use exit(EXIT_SUCCESS) I don't see Object with # has been destroyed printed on the screen as though the destructors are never called.
Is the previous object destroyed when the variable holding it gets assigned a new one using a copy constructor?
This question is moot because it is not possible to do so.
When you run
object_a = object_b;
this calls the assignment operator (not the copy constructor). It does not create or destroy any objects (unless your assignment operator does that).
In this case you haven't defined an assignment operator, so the default one is used, which overwrites object_3's ID with the other object's ID (which is 4). So when object_3 is destroyed it prints "Object with id 4 has been destroyed".
During some coding this morning I managed to enter and compile something akin to the following.
void a_function(void)
{
A_Class(std::string);
... //Other code continues
}
(names have been replaced to protect the identity of those classes involved)
What I had intended to have was a named instance A_Class an_instance(a_string); which I know would be in scope until the end of the function. What is the scope and lifetime of the A_Class created in this function?
I expect I could do something clever in the construction phase to make the lifetime almost anything I want. In this case the constructor makes a function call and a printf while the destructor does similar. Nothing invokes new, delete, malloc or anything of that sort.
Since C++98, a temporary lasts until the end of the full-expression, unless its lifetime is extended by binding to a reference.
The rules were different, as I recall, in ARM (the Annotated Reference Manual by Stroustrup and Ellis) C++.
It's not generally possible to figure out the standard's guarantees by just inspecting examples in a debugger or via trace statements, so the question has merit.
Only names have scope, so that concept is irrelevant here.
You're creating a temporary object.
Like all temporary objects, its lifetime lasts until the end of the full-expression where it was created, i.e until the ";" in this case, unless it's bound to a const reference variable.
The expression:
A_Class();
creates a temporary object of class A_Class. The temporary is destroyed after the evaluation of the full expression which lexicaly contains it -- in this case, the temporary will be destroyed immediately after its construction.
In an attempt to answer my own question,
Second Attempt
Updated test code now reads:
#include <iostream>
class Class_A
{
public:
Class_A(std::string a_string)
: saved_string(a_string)
{
std::cout << "Class_A Constructed:" << saved_string << std::endl;
}
~Class_A()
{
std::cout << "Class_A Deconstructed:" << saved_string << std::endl;
}
std::string saved_string;
};
class Class_B
{
public:
Class_B(std::string a_string)
: saved_string(a_string)
{
std::cout << "Class_B Constructed:" << saved_string << std::endl;
}
~Class_B()
{
std::cout << "Class_B Deconstructed:" << saved_string << std::endl;
}
std::string saved_string;
};
int main ()
{
std::cout << "Welcome to the lifetime test." << std::endl;
Class_A a_a("A");
Class_B a_b("B"); //Now I have to have () here too
std::cout << "Now for the interesting bit." << std::endl;
Class_A("a"); //Have to have the ()
Class_B("b");
std::cout << "Fin." << std::endl;
return 0;
}
Which when executed produces:
$ ./a.out
Welcome to the lifetime test.
Class_A Constructed:A
Class_B Constructed:B
Now for the interesting bit.
Class_A Constructed:a
Class_A Deconstructed:a
Class_B Constructed:b
Class_B Deconstructed:b
Fin.
Class_B Deconstructed:B
Class_A Deconstructed:A
So I can now see the lifetimes and I am compelled to use () or rather ("string") in the appropriate places. It is a little odd that a_a() is a function declaration inside a function in the First Attempt but `a_a_("A") is a constructor in the second. I was unaware that it was permitted to declare functions inside the definition of other functions but I suppose it make some sense for a few times when you might have some odd linkage going on in the background.
The lifetime of the unnamed temporary is only as long as the expression it is part of. Up until the ;.
First Attempt
I got as far as:
class Class_A
{
public:
Class_A()
{
std::cout << "Class_A Constructed" << std::endl;
}
~Class_A()
{
std::cout << "Class_A Deconstructed" << std::endl;
}
};
class Class_B
{
public:
Class_B()
{
std::cout << "Class_B Constructed" << std::endl;
}
~Class_B()
{
std::cout << "Class_B Deconstructed" << std::endl;
}
};
int main ()
{
std::cout << "Welcome to the lifetime test." << std::endl;
Class_A a_a();
Class_B a_b;
std::cout << "Now for the interesting bit." << std::endl;
Class_A(); //Have to have the ()
Class_B();
std::cout << "Fin." << std::endl;
return 0;
Which produces:
$ g++ main.cpp
$ ./a.out
Welcome to the lifetime test.
Class_B Constructed
Now for the interesting bit.
Class_A Constructed
Class_A Deconstructed
Class_B Constructed
Class_B Deconstructed
Fin.
Class_B Deconstructed
So I only have 1 Class_A constructed. Which is a little confusing as I would have expected a_a and a_b to have the same lifetimes. Now what have I done.
(update to follow I am sure)
It does seem to show that the calls to a constructor without a variable name only exist for the line/statement they are in (until the ;). This makes some sense when I think about functors, as pointed out by #douglas-o-moen
I ran the following code
#include <iostream>
using namespace std;
class Count
{
private:
int count;
public:
//Constructor
Count():count(0) { cout << "Constructor called" << endl; }
//Destructor
~Count() { cout << "Destructor called" << endl; }
//Display the value.
Count display()
{
cout << "The value of count is " << count << endl;
return *this;
}
};
int main()
{
Count C;
C.display();
}
Result :-
Constructor called
The value of count is 0
Destructor called
Destructor called
In the above case, the destructor is called twice, one for destruction of the "this" object and one for return from main.
Is my observation correct ??
Can anyone explain me also the temporary object created in this process like why it is created, if created??
You are returning a copy from display() method thus it needs to be destructed too. So, in your main you actually have 2 objects, one - implicitly.
The destructor is called twice because your display function returns a copy of the Count instance that it is called on. So that gets destroyed along with your C instance within main.
2 instances = 2 destructor calls.
If you specify and implement your function to return an instance of Count then it will do so. If you don't want this behaviour, then change the return type to void and don't return anything - then your code will contain just the one instance C.
Your code here
Count display() {
// ^^^^^
cout << "The value of count is " << count << endl;
return *this; // <<<<<<
}
creates an implicit copy of your instance that is returned and immediately destroyed, hence the second destructor call.
Yes correct.
Your function display() created a copy which made a second object.
if you return a reference you wouldn't get the copy.
Count& display() { return *this; }
Can someone tell why test(2) object is destroyed after test_method() call?
#include<iostream>
#include<string>
using namespace std;
class test
{
int n;
public:
test(int n) : n(n)
{
cout << "test: " << n << endl;
}
~test()
{
cout << "~test: " << n << endl;
}
test & test_method()
{
cout << "test_method: " << n << endl;
return *this;
}
};
int main(int argc, const char *argv[])
{
cout << "main start" << endl;
const test &test1 = test(1);
const test &test2 = test(2).test_method();
cout << "main end" << endl;
}
Output is:
main start
test: 1
test: 2
test_method: 2
~test: 2
main end
~test: 1
test(2).test_method() returns a reference, which is bound to test2, and then the object to which it refers is destroyed at the end of the full expression, since it is a temporary object. That should not be a surprise.
The real surprise is that test1 remains a valid reference, because it is directly bound to a temporary, and binding a temporary to a reference extends the lifetime of the temporary to that of the reference variable.
You only have to note that in the test(2) case, the temporary object isn't bound to anything. It's just used to invoke some member function, and then its job is done. It doesn't "babysit" member functions, or in other words, lifetime extension isn't transitive through all possible future references.
Here's a simple thought experiment why it would be impossible to actually have "arbitrary lifetime extension":
extern T & get_ref(T &);
{
T const & x = get_ref(T());
// stuff
// Is x still valid?
}
We have no idea if x remains valid beyond the first line. get_ref could be doing anything. If it's implemented as T & get_ref(T & x) { return x; }, we might hope for magic, but it could also be this:
namespace { T global; }
T & get_ref(T & unused) { return global; }
It's impossible to decide within the original translation unit whether anything needs to be extended or not. So the way the standard has it at present is that it's an entirely trivial, local decision, just made when looking at the reference declaration expression, what the lifetime of the temporary object in question should be.
Because the C++ standard requires this behavior. Give the object a name if you want it to persist. It will persist as long as the name.
Edit: You your example, test1 is the name that you gave to the first object, whereas the second object has obtained no name at all, and so it does not outlast evaluation of the expression.
In the below code, when I pass an unnamed A variable to the ctor of B, the variable is destructed after the line. According to this answer :
Temporary objects are destroyed at the
end of the full expression they're
part of. A full expression is an
expression that isn't a sub-expression
of some other expression. Usually this
means it ends at the ; (or ) for if, while, switch etc.)denoting the end
of the statement.
I get it but how can the class B know the value of its mamber_a variable, after it is destructed? I know that copy ctor of A is enver called. How is this possible?
#include <iostream>
using namespace std;
class A
{
int sign;
A();
const A & operator=(const A &);
public:
A(int x) : sign(x) {
cout << "A ctor : " << sign << endl;
}
void WriteA() const {
cout << sign << endl;
}
~A() {
cout << "A dtor : " << sign << endl;
}
A(const A &) {
cout << "A copied : " << sign << endl;
}
};
class B
{
int sign;
const A & member_a;
public:
B(const A & aa , int ww ) : sign (ww) ,member_a(aa) {
cout << "B ctor : " << sign << endl;
}
void WriteB() const {
cout << "Value of member_a :";
member_a.WriteA();
}
~B() {
cout << "B dtor : " << sign << endl;
}
};
int main() {
A a(10);
B b1(a,1);
b1.WriteB();
B b2(A(20),2);
b2.WriteB();
return 0;
}
The output is :
A ctor : 10
B ctor : 1
Value of member_a :10
A ctor : 20
B ctor : 2
A dtor : 20
Value of member_a :20 // Object A was destructed. Where does this 20 come from?
B dtor : 2
B dtor : 1
A dtor : 10
You have one of the tricky parts of C++
It is pure chance that member_a has the value 20. You are hitting what is refereed to as undefined behavior.
When a class retains a reference to an external object it is the responsibility of the programmer to make sure that the lifetime of the object lasts longer than the object being referred to. In this case when you call member_a.WriteA(); the a object has already been destroyed and thus you are accessing memory that may potentially not belong to you (in this case it just happens to be in a location nearby that has not been overridden (completely by chance)).
If you are going to retain a reference to an object. You can retain a const reference but sometimes it is best to make the parameter a normal reference so that you can not accidentally pass a temporary value (this does not always work as you may have to pass a const object by reference).
Using a reference to a destructed object is an "undefined behaviour".
In A's destructor, try setting "sign" to "-1" and see what happens. Odds are the call to "WriteB" will show that you have sent a message to a deceased object.
Now try putting a bunch of stack using code between the constructor of b2 and the call to b2.WriteB, for example a call to a subroutine. You will probably now find that the call prints something different.