2011-12-31 4 views
1

Код:Когда вызываются конструкторы и деструкторы копирования и почему?

#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; 
} 

Результат:

 
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 

я уже задал вопрос об управлении памятью этого кода, но после понимания ответов, я до сих пор есть проблемы с деструктор и конструктор копирования. По моему мнению, Picture temp = frame(my_pic) вызовет конструктор копирования.

Здесь возникает вопрос:

  1. Почему не конструктор копирования вызывается после Picture temp = frame(my_pic)
  2. и почему деструктор с именем?
  3. В Picture frame(const Picture& pic) будет вызван конструктор копирования, если функция вызывается? Я так считаю, потому что он возвращает значение «Изображение» по значению.
  4. Если я изменю Picture frame(const Picture& pic) на Picture frame(Picture p), будет ли конструктор копирования дважды вызываться при вызове функции?
  5. Когда будет вызываться конструктор копирования? Это произойдет, когда класс будет возвращен функцией по значению? Когда тогда класс передается функции по значению?
  6. Когда будет вызван деструктор? Это когда каждый раз, когда срок жизни переменной заканчивается? Означает ли это, что если передать переменную функции по значению, ее деструктор будет вызываться после выполнения функций?

Я сейчас испорчен конструктором копирования и деструктором, особенно когда у меня есть функция с возвращаемым значением и некоторые параметры, все переданные значениями.

Также, кто-нибудь поможет мне написать комментарий к каждой строке строк вывода? Это было бы очень полезно.

+3

Обратите внимание, что вызовы для копирования конструкторов и операторов присваивания могут быть свободно устранены оптимизатором компилятора, даже если они имеют побочные эффекты. Это единственные два, для которых компилятору разрешено нарушать правило as-if. (Конечно, компилятор по-прежнему должен не нарушать логику и сопоставлять вызовы ctors и dtors.) – sbi

+0

@outis: Нет. Возвращаемое выражение имеет тип 'Frame_Pic *'.'Frame_Pic' получен из' P_Node' и 'P_Node *' неявно конвертируется в 'Picture' через закрытый конструктор, который отлично подходит, поскольку' frame' объявлен другом. Тело возвращает «Изображение», так как объявляется функция. –

+0

@CharlesBailey, поэтому код не _wrong_ как таковой, но я определенно не назвал бы эту хорошую практику. – bames53

ответ

3

В ответ на ваши вопросы.

  1. Копия конструктор не вызывается после заявления Picture temp = frame(my_pic);, потому что у вас нет каких-либо заявлений, которые вызывают какие-либо копии после этого заявления.

  2. Три деструкторы Picture призваны уничтожить (в порядке убывания): temp, p в Frame_Pic указывает temp.p и my_pic. Ваш компилятор избегал генерации любых других временных объектов Picture.

  3. Да, конструктор копирования может быть вызван для инициализации возвращаемого значения Picture frame(const Picture& pic), но компилятор разрешен (и в этом случае), чтобы исключить копию и инициализировать возвращаемое значение непосредственно из выражения return.

  4. Да, дополнительный вызов конструктора копии может быть сгенерирован, если вы измените параметр для frame, который будет передан по значению, но если параметр инициализирован выражением, которое не является glvalue, относящимся к существующему объекту, аргумент может быть инициализированным непосредственно этим выражением, и копия завершена.

  5. Конструктор копирования вызывается всякий раз, когда объект типа класса фактически копируется. Это может быть при передаче функции или возврате из функции, но иногда компиляторам разрешено пропускать ненужные копии в этих сценариях.

  6. Да, деструктор вызывается всякий раз, когда объект типа класса уничтожается.Это верно для именованных переменных и временных рядов, генерируемых компилятором. Можно завершить время жизни объекта, не вызывая деструктор, например. мое повторное использование его памяти для другого объекта, но это очень особый случай.

+0

о ответе 1, если я пишу 'Picture a; Будет вызываться изображение b = a' Копировать константу изображения B. поэтому почему «Picture temp = frame (my_pic)» не вызывает его конструктор копирования? не 'frame' генерирует' Picture' и использует его для вызова «Constructor» Picture temp'? – shengy

+0

@shengy: 'Picture temp = frame (my_pic);' задает инициализацию копирования, которая _may_ вызывает конструктор копирования, но временный и конструктор копирования могут быть удалены, что здесь имеет место. Вы спросили, почему конструктор копирования не вызвал _after_ этот оператор, который является другим вопросом. –

1

Конструктор копирования не обязательно будет называться, когда вы думаете, что может или должно быть названо:

Следующие случаи могут привести к вызову конструктора копирования:

  1. Когда объект возвращается значение
  2. Когда объект передается (к функции) по значению в качестве аргумента
  3. Когда объект брошенной
  4. Когда объект захватывается
  5. Когда объект находится в списке инициализатора брекет огороженный

Эти случаи собирательно называется копирования инициализации и эквивалентны: T x = a;

Это, однако, не гарантирует, что конструктор копирования будет называться в этих случаях поскольку Стандарт C++ позволяет компилятору оптимизировать копию далеко в некоторых случаях, один пример, являющийся return value optimization (иногда называют как RVO).

From Wikipedia.

Деструктор для всего, что находится на стеке вызывается, когда он выходит из области видимости.

+0

, но почему не конструируется копия рисунка, когда я использую оператор «Picture temp = frame (my_pic)»? – shengy

+0

[Возможно, из-за этого, о котором упоминалось в моем ответе.] (Http://en.wikipedia.org/wiki/Return_value_optimization) –

+0

@shengy: Но это называется, см. Строку 2 выведенного вами вывода. –

0

Примечание: во всех ответах о том, что конструктор копирования будет называться, возможно, что этого не произойдет, потому что компилятор сделал некоторую оптимизацию.

1) Почему заказчик не вызван после Picture temp = frame(my_pic)?

Picture temp = frame (my_pic); это последняя строка перед оператором return, поэтому все, что происходит после того, как программа снесена (уничтожены деструкторы, стек и куча), заканчивается.

2) Почему деструктор называется?

Вызывается деструктор (в каждом случае здесь), потому что программа закрыта. Примечание. Хотя это происходит в конце программы, это не означает, что вы не должны очищать себя после себя!

3) В Picture frame(const Picture& pic) будет вызываться конструктор копирования, если функция вызывается?

Нет. Вы не сделали копию, которую передали ссылку на то, где она находится, и создали новую, и компилятор оптимизирует копию при возврате.

4) Если я изменяю Picture frame(const Picture& pic) к Picture frame(Picture p), будет конструктор копирования будет вызван дважды, когда функция вызывается?

№ Это может быть вызвано, когда вы вводите функцию, но компилятор будет оптимизировать копию в обратном направлении.

5) Когда будет вызываться конструктор копирования? Это произойдет, когда класс будет возвращен функцией по значению? Когда тогда класс передается функции по значению?

Конструктор копирования будет вызываться в обоих случаях.

6) Когда будет вызван деструктор? когда каждый раз, когда срок жизни переменной заканчивается? Означает ли это, что если передать переменную функции по значению, ее деструктор будет вызван после выполнения функции?

Когда деструктор будет вызываться, когда объект будет уничтожен. Это может быть связано с тем, что вы его уничтожили, или функция, содержащая его, возвращает (заканчивается), а ее переменные/объекты удаляются из стека или в некоторых случаях (в конце программы) из кучи.

+0

'Picture temp = frame (my_pic);' is _copy initialization_, это не обязательно приводит к вызову конструктора копирования. В этом случае конструктор копирования для 'Picture' генерируется только для инициализации в списке инициализаторов членов для' Frame_Pic': '[...]: p (pic) {' –

+0

@Charles Bailey Typo отредактировал ответ, приятно поймать ! Благодарю. – vdbuilder

Смежные вопросы