Какова вся суета о конструкторах копии C++? [дубликат]

15
задан Community 23 May 2017 в 12:02
поделиться

5 ответов

Конструкторы копирования и операторы присваивания очень важны в C ++, потому что язык имеет «семантику копирования», то есть когда вы передаете параметр или сохраняете значение в контейнере, копию объект передан или сохранен. Как C ++ может сделать копию или назначить объекту? Для собственных типов он знает сам, но для пользовательских типов вместо этого он автоматически генерирует построение или присваивание копии для каждого элемента.

Более явно, если вы объявляете, например:

class P2d
{
    public:
        double x, y;
        P2d(double x, double y) : x(x), y(y)
        { }
};

компилятор C ++ автоматически завершает ваш код до

class P2d
{
    public:
        double x, y;
        P2d(double x, double y) : x(x), y(y)
        { }

        P2d(const P2d& other) : x(other.x), y(other.y)
        { }

        P2d& operator=(const P2d& other)
        {
            x = other.x;
            y = other.y;
            return *this;
        }
};

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

Вы должны понимать, что C ++ делает множество копий объектов, и вы должны понимать, какой тип копирования он будет делать для классов, которые вы определили.Если автоматически сгенерированная копия вам не нужна, вы должны либо предоставить свою собственную реализацию, либо сообщить компилятору, что копирование должно быть запрещено для вашего класса.

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

Например:

class Window
{
    public:
        WindowType t;
        Window *parent,
               *prev_in_parent, *next_in_parent,
               *first_children, *last_children;
        Window(Window *parent, WindowType t);
        ~Window();

    private:
        // TABOO! - declared but not implemented
        Window(const Window&); // = delete in C++11
        Window& operator=(const Window&); // = delete in C++11
};

Если последняя часть кажется абсурдной (как вы можете делать копии в реализации по ошибке), обратите внимание, что в C ++ очень легко сделать дополнительные копии по ошибке, потому что язык построен вокруг концепция копирования вещей вокруг.

Золотое правило состоит в том, что если в вашем классе есть деструктор (потому что он должен выполнить некоторую очистку), то, скорее всего, копирование по каждому члену не является правильным решением ... а также, если у вас есть особая логика чтобы сделать копирующую конструкцию, аналогичная логика, вероятно, понадобится также и при присваивании (и наоборот). Итак, правило, известное как Большая тройка , гласит, что либо в вашем классе нет настраиваемого деструктора, либо конструктора копирования, либо оператора присваивания, либо в вашем классе должны быть все три из них.

Это правило настолько важно, что, например, если в каком-то особом случае вы получите класс, которому просто нужен деструктор (я не могу придумать разумный случай ...но давайте просто скажем, что вы его нашли), тогда, пожалуйста, не забудьте добавить в качестве комментария, что вы подумали об этом и знаете, что неявно сгенерированный конструктор копирования и операторы присваивания в порядке. Если вы не добавите примечания о двух других, любой, кто прочитает ваш код, подумает, что вы просто забыли о них.

ОБНОВЛЕНИЕ

C ++ развивается, и хотя большая часть того, что здесь сказано, все еще актуальна, теперь язык предоставляет лучший способ сообщить компилятору, что копирование и присваивание недопустимо.

Новый синтаксис (действителен с C ++ 11):

struct Window {
    ...
    Window(const Window&) = delete;
    Window& operator=(const Window&) = delete;
};
29
ответ дан 1 December 2019 в 00:29
поделиться

Каждый класс в C ++ имеет неявный конструктор копирования, который делает неглубокую копию объекта. Shallow означает, что он копирует значение членов. Итак, если есть указатель, значение указателя копируется, поэтому оба объекта указывают на одно и то же.

В большинстве случаев это нежелательно, поэтому вам нужно определить свой собственный конструктор копирования.

Часто вы даже не хотите создавать копии своего объекта, поэтому лучше всего объявить конструктор копирования, но не определять его. (Пустое объявление в заголовочном файле).

Затем, если вы случайно создадите копию (например, вернув объект, забыли & при объявлении метода по ссылке и т. Д.), Вы получите ошибку компоновщика.

Если вы объявите конструктор копирования закрытым, вы также получите ошибку компилятора (если используется вне класса).

Подводя итог: всегда хорошо объявлять конструктор копирования явно - особенно если он вам вообще не нужен: просто напишите

private:
  MyClass(const MyClass&);

в определении вашего класса, чтобы отключить конструктор копирования вашего класса.

4
ответ дан 1 December 2019 в 00:29
поделиться
​​

Просто: когда C ++ использует конструктор копирования по умолчанию, он копирует указатель, но не данные, на которые указывают. Результат: два объекта, указывающих на одни и те же данные. Если оба думают, что они владеют этими данными, и удаляют указатель при вызове их деструктора, у вас куча проблем ...

7
ответ дан 1 December 2019 в 00:29
поделиться

Почему именно конструкторы копирования C ++ так важно?

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

  • Не имеют указателей (например, старые версии BASIC), и в этом случае копирование объектов всегда безопасно, либо
  • Не иметь ничего , кроме указателей / ссылок (например, Java, Python), и в этом случае копирование выполняется редко, а затем может быть выполнено с помощью copy () или clone () метод.

C ++ предпочитает семантику значений, но также использует множество указателей, что означает, что:

  • Объекты копируются много, и
  • Вы должны указать, хотите ли вы неглубокую или глубокую копию, по причинам, которые есть у других. упомянул.
6
ответ дан 1 December 2019 в 00:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: