Reputation: 9749
The code is:
#include <iostream>
class P_Node {
friend class Picture;
protected:
P_Node() : use(1) {}
virtual ~P_Node() {}
private:
int use;
};
class Picture {
friend Picture frame(const Picture&);
public:
Picture() : p(new P_Node) {
std::cout << "Constructor\t" << "Picture::Picture()" << "\tcalled" << std::endl;
std::cout << "Picture p count\t" << p->use << std::endl;
}
Picture(const Picture& orig) : p(orig.p) {
std::cout << "Copy Constructor\t" << "Picture::Picture(const Picture&)" << "\tcalled" << std::endl;
std::cout << "Picture p count\t" << p->use << std::endl;
orig.p->use++;
}
~Picture() {
std::cout << "Destructor\t" << "Picture::~Picture()" << "\tcalled" << std::endl;
std::cout << "Picture p count before decrease\t" << p->use << std::endl;
if(--p->use == 0) {
std::cout << "Picture p count after decrease\t" << p->use << std::endl;
std::cout << "Deleted" << std::endl;
delete p;
}
}
Picture& operator=(const Picture& orig) {
std::cout << "operator=\t" << "Picture& Picture::operator=(const Picture& orig)" << "\tcalled" << std::endl;
std::cout << "Picture p count before decrease\t" << p->use << std::endl;
orig.p->use++;
if(--p->use == 0) {
std::cout << "Picture p count after decrease\t" << p->use << std::endl;
std::cout << "Deleted" << std::endl;
delete p;
}
p = orig.p;
return *this;
}
private:
Picture(P_Node* p_node) : p(p_node) {
std::cout << "Picture::Picture(P_Node* p_node)\tcalled" << std::endl;
}
P_Node *p;
};
class Frame_Pic : public P_Node {
friend Picture frame(const Picture&);
private:
Frame_Pic(const Picture& pic) : p(pic) {
std::cout << "Frame_Pic::Frame_Pic(const Picture& orig)" << "\tcalled" << std::endl;
}
Picture p;
};
Picture frame(const Picture& pic) {
return new Frame_Pic(pic);
}
int main() {
Picture my_pic;
Picture temp = frame(my_pic);
return 0;
}
The result is:
Constructor Picture::Picture() called Picture p count 1 Copy Constructor Picture::Picture(const Picture&) called Picture p count 1 Frame_Pic::Frame_Pic(const Picture& orig) called Picture::Picture(P_Node* p_node) called Destructor Picture::~Picture() called Picture p count before decrease 1 Picture p count after decrease 0 Deleted Destructor Picture::~Picture() called Picture p count before decrease 2 Destructor Picture::~Picture() called Picture p count before decrease 1 Picture p count after decrease 0 Deleted
I previously asked a question about memory management of this code, but after understanding the answers, I still have a problem with the destructor and the copy constructor. In my understanding, Picture temp = frame(my_pic)
will call the copy constructor.
Here comes the question:
Picture temp = frame(my_pic)
Picture frame(const Picture& pic)
, will the copy constructor be called if the function is called? I believe so, because it returns a 'Picture' by value.Picture frame(const Picture& pic)
to Picture frame(Picture p)
will the copy constructor called twice when the function is called?I'm messed up with the copy constructor and the destructor right now, especially when I have a function with a return value, and some parameters all passed by values.
Also, will anyone help me to write a comment on each line of the output strings? That would be very helpful.
Upvotes: 1
Views: 4524
Reputation: 12994
Note: In all answers stating that the copy constructor will be called it is possible that it won't be because the compiler did some optimization.
1) Why isn't the copy consturctor called after
Picture temp = frame(my_pic)
?
Picture temp = frame(my_pic); is last line before the return statement, therefore all that happens after it is the program is torn down (destructors called, stack and heap cleared) and ends.
2) Why is the destructor called?
The destructor (in each case here) is being called because the program closed. Note: Although this does happen at the end of the program, it does not mean that you shouldn't cleanup after yourself!
3) In
Picture frame(const Picture& pic)
, will the copy constructor be called if the function is called?
No. You didn't make a copy you passed a reference to where one is and created a new one and the compiler will optimize out the copy on the return.
4) If I change
Picture frame(const Picture& pic)
toPicture frame(Picture p)
, will the copy constructor be called twice when the function is called?
No. It may be called when you enter the function but the compiler will optimize out the copy in the return.
5) When will the copy constructor be called? Will it happen when the class is returned by a function by value? When then class is passed to a function by value?
The copy constructor will be called in both cases.
6) When will the destructor be called? when each time the variable's lifetime is ended? Does that mean if I pass a variable to a function by value, its destructor will be called after the function's execution?
When the destructor will be called when the object is destroyed. That could be because you destroyed it or the function containing it returns (ends) and its variables/objects are removed from the stack or in some cases (at the end of the program) from the heap.
Upvotes: 0
Reputation: 793239
In answer to your questions.
The copy constructor isn't called after the statement Picture temp = frame(my_pic);
because you don't have any statements that cause any copies after that statement.
The three destructors for Picture
are called to destroy (in order): temp
, p
in the Frame_Pic
pointed to by temp.p
and my_pic
. Your compiler has avoided generating any other temporary Picture
objects.
Yes, a copy constructor may be called to initialize the return value of Picture frame(const Picture& pic)
but the compiler is allowed (and does in the case) to eliminate the copy and initialize the return value directly from the return expression.
Yes, an additional copy constructor call may be generated if you change the parameter for frame
to be passed by value but if the parameter is initialized with an expression that isn't a glvalue referring to an existing object the argument might be initialized directly with that expression and the copy elided.
A copy constructor is called whenever an object of class type is actually copied. This may be when being passed to a function or returned from a function but sometimes compilers are allowed to omit unnecessary copies in these scenarios.
Yes, a destructor is called whenever an object of class type is destroyed. This is true for named variables and temporaries generated by the compiler. It is possible to end an object's lifetime without calling a destructor, e.g. my re-using its memory for another object, but this is very much a special case.
Upvotes: 4
Reputation: 6578
The copy constructor will not necessarily be called whenever you think it might or should be called:
The following cases may result in a call to a copy constructor:
- When an object is returned by value
- When an object is passed (to a function) by value as an argument
- When an object is thrown
- When an object is caught
- When an object is placed in a brace-enclosed initializer list
These cases are collectively called copy-initialization and are equivalent to:
T x = a
;It is however, not guaranteed that a copy constructor will be called in these cases, because the C++ Standard allows the compiler to optimize the copy away in certain cases, one example being the return value optimization (sometimes referred to as RVO).
The destructor for anything that's on the stack is called when it goes out of scope.
Upvotes: 1