Когда вызываются конструкторы и деструкторы копирования и почему? - PullRequest
1 голос
/ 31 декабря 2011

Код:

#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) будет ли вызываться конструктор копирования, если вызывается функция?Я верю в это, потому что он возвращает 'Picture' по значению.
  4. Если я изменю Picture frame(const Picture& pic) на Picture frame(Picture p), будет ли вызываться конструктор копирования при вызове функции?
  5. Когда будетконструктор копирования будет называться?Произойдет ли это, когда класс будет возвращен функцией по значению?Когда класс передается функции по значению?
  6. Когда будет вызван деструктор?Это когда каждый раз заканчивается время жизни переменной?Означает ли это, что если я передам переменную в функцию по значению, ее деструктор будет вызван после выполнения функций?

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

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

Ответы [ 3 ]

4 голосов
/ 31 декабря 2011

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

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

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

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

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

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

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

1 голос
/ 31 декабря 2011

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

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

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

Эти случаи все вместе называются copy-initialization и эквивалентны: T x = a;

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

Из Википедии.

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

0 голосов
/ 31 декабря 2011

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

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

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

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

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

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

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

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

Нет. Он может вызываться при входе в функцию, но компилятор оптимизирует копию при возврате.

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...