Скажем, у меня есть класс, где единственный член данных - это что-то вроде std::string
или std::vector
. Должен ли я предоставить команду "Копировать конструктор", "Деструктор" и "Назначение"?
В каких обстоятельствах я должен предоставить оператора присваивания, конструктора копирования и деструктора для моего класса С++?
Ответ 1
Если ваш класс содержит только векторные/строковые объекты в качестве своих членов данных, вам не нужно их реализовывать. Классы С++ STL (например, vector, string) имеют собственный экземпляр ctor, перегруженный оператор присваивания и деструктор.
Но если ваш класс динамически распределяет память в конструкторе, то наивная мелкая копия приведет к проблемам. В этом случае вам придется реализовать copy ctor, перегруженный оператор присваивания и деструктор.
Ответ 2
Обычное эмпирическое правило гласит: если вам нужен один из них, тогда вам все они нужны.
Однако не все классы нуждаются в них. Если в классе нет ресурсов (в частности, памяти), с ними все будет в порядке. Например, класс с единственным компонентом string
или vector
им действительно не нужен - если вам не требуется какое-то специальное поведение при копировании (по умолчанию будет только скопировать элементы).
Ответ 3
Конструктор копии по умолчанию скопирует вектор, если он объявлен по значению. Остерегайтесь, если вы сохранили указатели в своем векторе, в таком случае вам необходимо указать конкретное поведение для копирования/назначения/уничтожения, чтобы избежать утечек памяти или нескольких удалений.
Ответ 4
Нет, но есть ряд причин, по которым вы не должны позволять компилятору автоматически генерировать эти функции.
По моему опыту, всегда лучше определить их самостоятельно и привыкнуть к тому, чтобы они поддерживались при изменении класса. Во-первых, вы можете захотеть поставить точку останова при вызове определенного ctor или dtor. Кроме того, их определение не может привести к разрыву кода, поскольку компилятор будет генерировать встроенные вызовы членам ctor и dtor (у Скотта Мейерса есть раздел об этом).
Также вы иногда хотите запретить копии и назначения копирования по умолчанию. Например, у меня есть приложение, которое хранит и обрабатывает очень большие блоки данных. У нас обычно есть эквивалент вектора STL, содержащего миллионы 3D-точек, и это было бы катастрофой, если бы мы разрешили копирование этих контейнеров. Таким образом, операторы ctor и присваивания объявляются частными и не определены. Таким образом, если кто-нибудь пишет
class myClass {
void doSomething(const bigDataContainer data); // not should be passed by reference
}
тогда они получат ошибку компилятора. Наш опыт заключается в том, что явный метод get() или clone() гораздо менее подвержен ошибкам.
Итак, у всех есть много причин, чтобы избежать автогенерируемых функций компилятора.
Ответ 5
Я могу вспомнить несколько случаев, когда вам нужно написать свою собственную большую тройку. Все стандартные контейнеры умеют копировать и уничтожать самих себя, поэтому вам не обязательно их писать. Здесь, как знать, когда вы делаете:
Имеет ли мой класс какие-либо ресурсы?
Семантика копии по умолчанию для указателей - это скопировать значение указателя, а не то, на что оно указывает. Если вам нужно что-то глубоко скопировать, даже если оно хранится в стандартном контейнере, вам нужно написать собственный конструктор копий и оператор присваивания. Вам также нужно написать собственный деструктор, чтобы правильно освободить эти ресурсы.
Возможно, кто-то наследует мой класс?
Базовые классы нуждаются в деструкторе. Herb Sutter рекомендует сделать их как public
, так и virtual
(наиболее распространенный случай) или protected
и не виртуальный, в зависимости от того, что вы хотите сделать с ними. Деструктор, созданный компилятором, является открытым и не виртуальным, поэтому вам придется писать свои собственные, даже если в нем нет никакого кода. (Примечание: это не означает, что вам нужно написать конструктор копирования или оператор присваивания.)
Должен ли я запретить пользователю копировать объекты моего класса?
Если вы не хотите, чтобы пользователь копировал ваши объекты (может быть, это слишком дорого), вам нужно объявить конструктор копирования и операторы присваивания либо protected
, либо private
. Вам не нужно их реализовывать, если они вам не нужны. (Примечание: это не означает, что вам нужно написать деструктор.)
Нижняя строка:
Самое главное - понять, что сделает создатель, созданный компилятором, оператор присваивания и деструктор. Вам не нужно их бояться, но вам нужно подумать о них и решить, подходит ли их поведение для вашего класса.
Ответ 6
для этого контейнера понадобится элемент "copy constructible", и если вы не предоставите конструктор копирования, он вызовет конструктор копии по умолчанию вашего класса, выведя его из ваших членов класса (мелкая копия).
простое объяснение о конструкторе копии по умолчанию находится здесь: http://www.fredosaurus.com/notes-cpp/oop-condestructors/copyconstructors.html
это так с деструктором, контейнер должен иметь доступ к вашему деструктору или деструктору класса по умолчанию, если вы его не предоставляете (т.е. он не будет работать, если вы объявите деструктор как закрытый)
Ответ 7
вам нужно предоставить их, если они вам понадобятся. или возможных пользователей ваших классов. destructor всегда должен, а конструкторы копирования и оператор присваивания автоматически создаются компилятором. (По крайней мере, MSVC)
Ответ 8
Когда у вас есть класс, требующий глубоких копий, вы должны определить их.
В частности, любой класс, содержащий указатели или ссылки, должен содержать их:
class foo {
private:
int a,b;
bar *c;
}
Субъективно, я бы сказал, всегда определяйте их, поскольку поведение по умолчанию, предоставляемое сгенерированной версией компилятора, может быть не таким, что вы ожидаете/хотите.
Ответ 9
Не для строк или векторов, поскольку тривиальные конструкторы/деструкторы и т.д. сделают все возможное.
Если ваш класс имеет указатели на другие данные и нуждается в глубоких копиях, или если ваш класс содержит ресурс, который должен быть освобожден или должен быть скопирован особым образом.