Когда мы должны использовать конструкторов копии?

Я знаю, что компилятор C++ создает конструктора копии для класса. В этом случае мы должны записать пользовательскому конструктору копии? Можно ли дать некоторые примеры?

80
задан harshvchawla 25 July 2017 в 22:14
поделиться

5 ответов

Конструктор копирования, создаваемый компилятором, выполняет копирование по членам. Иногда этого недостаточно. Например:

class Class {
public:
    Class( const char* str );
    ~Class();
private:
    char* stored;
};

Class::Class( const char* str )
{
    stored = new char[srtlen( str ) + 1 ];
    strcpy( stored, str );
}

Class::~Class()
{
    delete[] stored;
}

в этом случае копирование члена stored не дублирует буфер (копируется только указатель), поэтому первая уничтоженная копия, разделяющая буфер, успешно вызовет delete[], а вторая столкнется с неопределенным поведением. Вам нужен конструктор копирования глубокого копирования (и оператор присваивания тоже).

Class::Class( const Class& another )
{
    stored = new char[strlen(another.stored) + 1];
    strcpy( stored, another.stored );
}

void Class::operator = ( const Class& another )
{
    char* temp = new char[strlen(another.stored) + 1];
    strcpy( temp, another.stored);
    delete[] stored;
    stored = temp;
}
70
ответ дан 24 November 2019 в 09:55
поделиться

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

2
ответ дан 24 November 2019 в 09:55
поделиться

Меня немного раздражает, что не было приведено правило Правило пяти.

Это правило очень простое:

Правило пяти:
Всякий раз, когда вы пишете один из Destructor, Copy Constructor, Copy Assignment Operator, Move Constructor или Move Assignment Operator, вам, вероятно, нужно написать остальные четыре.

Но есть и более общее правило, которому вы должны следовать, вытекающее из необходимости писать безопасный от исключений код:

Каждый ресурс должен управляться выделенным объектом

Здесь @sharptooth код все еще (в основном) в порядке, но если бы он добавил второй атрибут к своему классу, это было бы не так. Рассмотрим следующий класс:

class Erroneous
{
public:
  Erroneous();
  // ... others
private:
  Foo* mFoo;
  Bar* mBar;
};

Erroneous::Erroneous(): mFoo(new Foo()), mBar(new Bar()) {}

Что происходит, если new Bar бросается? Как вы удалите объект, на который указывает mFoo? Есть решения (try/catch на уровне функций ...), но они просто не масштабируются.

Правильный способ справиться с ситуацией - использовать соответствующие классы вместо сырых указателей.

class Righteous
{
public:
private:
  std::unique_ptr<Foo> mFoo;
  std::unique_ptr<Bar> mBar;
};

С той же реализацией конструктора (или фактически, используя make_unique), я теперь имею безопасность исключений бесплатно!!! Разве это не здорово? И самое главное, мне больше не нужно беспокоиться о правильном деструкторе! Правда, мне придется написать свои собственные Copy Constructor и Assignment Operator, потому что unique_ptr не определяет эти операции... но это здесь не важно ;)

И поэтому, sharptooth's class revisited:

class Class
{
public:
  Class(char const* str): mData(str) {}
private:
  std::string mData;
};

Не знаю как вам, а мне мой проще ;)

44
ответ дан 24 November 2019 в 09:55
поделиться

Часто рекомендуется отключить ctor и operator = для копирования, если это специально не требуется классу. Это может предотвратить неэффективность, например передачу аргумента по значению, когда предполагается ссылка. Также методы, созданные компилятором, могут быть недопустимыми.

0
ответ дан 24 November 2019 в 09:55
поделиться

Если у вас есть класс с динамически распределяемым содержимым. Например, вы храните название книги как char * и устанавливаете название с помощью new, копирование не будет работать.

Вам придется написать конструктор копирования, который делает title = new char[length+1], а затем strcpy(title, titleIn). Конструктор копирования будет просто выполнять "неглубокое" копирование.

6
ответ дан 24 November 2019 в 09:55
поделиться
Другие вопросы по тегам:

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