Чтение онлайн

на главную - закладки

Жанры

Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ

Майерс Скотт

Шрифт:

public:

...

Customer(const Customer& rhs);

Customer& operator=(const Customer& rhs);

...

private:

std::string name;

};

Customer::Customer(const Customer& rhs)

: name(rhs.name) // копировать данные rhs

{

logCall(“Конструктор копирования Customer”);

}

Customer& Customer::operator=(const Customer& rhs)

{

logCall(“Копирующий
оператор присвоения Customer”);

name = rhs.name; // копировать данные rhs

return *this; // см. правило 10

}

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

class Date {...}; // для даты и времени

class Customer {

public:

... // как раньше

private:

std::string name;

Date lastTransaction;

};

С этого момента существующие функции копирования копируют только часть объекта, именно поле name, но не поле lastTransaction. Однако большинство компиляторов ничего не скажут об этом даже при установке максимального уровня диагностики (см. также правило 53). Вот к чему приводит самостоятельное написание функций копирования. Вы отвергаете функции, которые генерирует компилятор, поэтому он не сообщает, что ваш код не полон. Решение очевидно: если вы добавляете новый член в класс, то должны обновить и копирующие функции (а также все конструкторы [см. правила 4 и 45] и все нестандартные варианты operator= в классе [пример в правиле 10]; если вы забудете, то компилятор вряд ли напомнит).

Одним из наиболее коварных случаев проявления этой ситуации является наследование. Рассмотрим пример:

class PriorityCustomer: public Customer { // производный класс

public:

...

PriorityCustomer(const PriorityCustomer& rhs);

PriorityCustomer& operator=(const PriorityCustomer& rhs);

...

private:

int priority;

};

PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs)

: priority(rhs.priority)

{

logCall(“Конструктор копирования PriorityCustomer”);

}

PriorityCustomer&

PriorityCustomer::operator=(const PriorityCustomer& rhs)

{

logCall(“Оператор присваивания PriorityCustomer”);

priority = rhs. Priority;

return *this;

}

На первый взгляд, копирующие функции в классе PriorityCustomer копируют все его члены, но приглядитесь внимательнее. Да, они копируют данные-члены, которые объявлены в PriorityCustomer, но каждый объект PriorityCustomer также содержит члены, унаследованные от Customer, а они-то не копируются вовсе! Конструктор копирования PriorityCustomer не специфицирует аргументы, которые должны быть переданы конструктору его базового класса (то есть не упоминает Customer в своем списке инициализации членов), поэтому часть Customer объекта PriorityCustomer будет инициализирована конструктором Customer, не принимающим аргументов, конструктором по умолчанию (если он отсутствует, то такой код просто не скомпилируется). Этот конструктор выполняет инициализацию по умолчанию членов name и lastTransaction.

Для оператора присваивания PriorityCustomer ситуация мало чем отличается. Он не выполняет никаких попыток модифицировать данные-члены базового класса, поэтому они остаются неизменными.

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

PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs)

: Customer(rhs), // вызвать копирующий конструктор

// базового класса

priority(rhs.priority)

{

logCall(“Конструктор копирования PriorityCustomer”);

}

PriorityCustomer&

PriorityCustomer::operator=(const PriorityCustomer& rhs)

{

logCall(“Оператор присваивания PriorityCustomer”);

Customer::operator=(rhs); // присвоить значения данным-членам

// базового класса

priority = rhs. Priority;

return *this;

}

Значение фразы «копировать все части» в заголовке этого параграфа теперь должно быть понятно. Когда вы пишете копирующие функции, убедитесь, что (1) копируются все локальные данные-члены и (2) вызываются соответствующие копирующие функции всех базовых классов.

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

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

Поделиться:
Популярные книги

Мужчина моей судьбы

Ардова Алиса
2. Мужчина не моей мечты
Любовные романы:
любовно-фантастические романы
8.03
рейтинг книги
Мужчина моей судьбы

Разбуди меня

Рам Янка
7. Серьёзные мальчики в форме
Любовные романы:
современные любовные романы
остросюжетные любовные романы
5.00
рейтинг книги
Разбуди меня

Неверный

Тоцка Тала
Любовные романы:
современные любовные романы
5.50
рейтинг книги
Неверный

На границе империй. Том 9. Часть 2

INDIGO
15. Фортуна дама переменчивая
Фантастика:
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 9. Часть 2

Небо для Беса

Рам Янка
3. Самбисты
Любовные романы:
современные любовные романы
5.25
рейтинг книги
Небо для Беса

Жребий некроманта 3

Решетов Евгений Валерьевич
3. Жребий некроманта
Фантастика:
боевая фантастика
5.56
рейтинг книги
Жребий некроманта 3

Черный Маг Императора 7 (CИ)

Герда Александр
7. Черный маг императора
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Черный Маг Императора 7 (CИ)

Его маленькая большая женщина

Резник Юлия
Любовные романы:
современные любовные романы
эро литература
8.78
рейтинг книги
Его маленькая большая женщина

Зауряд-врач

Дроздов Анатолий Федорович
1. Зауряд-врач
Фантастика:
альтернативная история
8.64
рейтинг книги
Зауряд-врач

Законы Рода. Том 4

Flow Ascold
4. Граф Берестьев
Фантастика:
юмористическое фэнтези
аниме
5.00
рейтинг книги
Законы Рода. Том 4

Проводник

Кораблев Родион
2. Другая сторона
Фантастика:
боевая фантастика
рпг
7.41
рейтинг книги
Проводник

Возмездие

Злобин Михаил
4. О чем молчат могилы
Фантастика:
фэнтези
7.47
рейтинг книги
Возмездие

Восход. Солнцев. Книга XI

Скабер Артемий
11. Голос Бога
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Восход. Солнцев. Книга XI

Курсант: Назад в СССР 10

Дамиров Рафаэль
10. Курсант
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Курсант: Назад в СССР 10