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:
- Why isn’t the copy constructor called after
Picture temp = frame(my_pic) - and why is the destructor called?
- In
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. - If I change
Picture frame(const Picture& pic)toPicture frame(Picture p)will the copy constructor called twice when the function is called? - 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?
- When will the destructor be called? Is it when each time a 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 functions execution?
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.
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
Pictureare called to destroy (in order):temp,pin theFrame_Picpointed to bytemp.pandmy_pic. Your compiler has avoided generating any other temporaryPictureobjects.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
frameto 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.