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

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

Жанры

C++. Сборник рецептов

Когсуэлл Джефф

Шрифт:

bool RecordSet::getFieldVal(int i, std::string& s) const {

 fieldArray_[i] = s; // Ой, я не это имел в виду

 // ...

}

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

В классе

RecordSet
(в таком, как (схематичный) класс в примере 15.4) вам, вероятно, потребовалось бы перемещаться туда-сюда по набору записей, используя понятие «текущей» записи. Простой способ заключается в применении переменной-члена
целого типа, содержащей номер текущей записи; ваши функции-члены, предназначенные для перемещения текущей записи вперед-назад, должны увеличивать или уменьшать это значение.

void RecordSet::gotoNextPecord const {

 if (curIndex_ >= 0 && curIndex_ < numRecords_-1)

++curIndex_;

}

void RecordSet::gotoPrevRecord const {

 if (curIndex_ > 0)

– -curIndex_;

}

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

RecordSet
не смогут перемещаться по объекту
const RecordSet
. Это исключение из правил работы с константными функциями-членами является вполне разумным, поэтому C++ имеет механизм его поддержки: ключевое слово
mutable
.

Для того чтобы

curIndex_
можно было обновлять в константной функции-члене, объявите ее с ключевым словом mutable в объявлении класса.

mutable int curIndex_;

Это позволит вам модифицировать

curIndex_
в любом месте. Однако этой возможностью следует пользоваться разумно, поскольку это действует на вашу функцию так, как будто она становится с этого момента неконстантной.

Применение ключевого слова

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

15.5. Написание оператора, не являющегося функцией-членом

Проблема

Необходимо написать бинарный оператор, и вы не можете или не хотите сделать его функцией-членом класса.

Решение

Используйте ключевое слово

operator
, временную переменную и конструктор копирования для выполнения основной работы и возвратите временный объект. В примере 15.5 приводится простой оператор конкатенации строк для пользовательского класса
String
.

Пример 15.5. Конкатенация с использованием оператора не члена

#include <iostream>

#include <cstring>

class String { // Предположим, что объявление класса String содержит,

// по крайней мере, все, что указанно ниже

public:

 String;

 String(const char* p);

 String(const String& orig);

 ~String {delete buf_;}

 String& append(const String& s);

 size_t length const;

 const char* data const;

 String& operator=(const String& orig);

 // ...

};

String operator+(const String& lhs, const String& rhs) {

 String tmp(lhs); //
Сконструировать временный объект с помощью

// конструктора копирования

 tmp.append(rhs); // Использовать функцию-член для выполнения реальной

// работы

 return(tmp); // Возвратить временный объект

}

int main {

 String s1("banana ");

 String s2("rancher");

 String s3, s4, s5, s6;

 s3 = s1 + s2; // Работает хорошо, но с сюрпризами

 s4 = s1 + "rama"; // Автоматически конструируется "rama", используя

// конструктор String(const char*)

 s5 = "ham " + s2; // Круто, то же самое можно делать даже

 s6 = s1 + "rama " + s2; // с другим операндом

 std::cout << "s3 = " << s3.data << '\n';

 std::cout << "s4 = " << s4.data << '\n';

 std::cout << "s5 = " << s5.data << '\n';

 std::cout << "s6 = " << s6.data << '\n';

}

Обсуждение

Независимый оператор объявляется и определяется подобно оператору функции-члена. В примере 15.5 я мог бы реализовать

operator+
как функцию-член, объявляя ее следующим образом.

String operator+(const String& rhs);

В большинстве случаев это будет работать одинаково, независимо от того, определяется ли

operator+
как функция-член или нет, однако существует, по крайней мере, две причины, по которым желательно реализовать его не как функцию-член. Первая причина концептуальная, имеет ли смысл иметь оператор, который возвращает новый, отличный от других объект?
operator+
, реализованный как функция-член, не проверяет и не изменяет состояние объекта. Это служебная функция общего назначения, которая в данном случае работает со строками типа
String
и, следовательно, не должна являться функцией членом.

Вторая причина техническая. При использовании оператора-члена вы не сможете выполнить следующую операцию (из приведенного выше примера).

s5 = "ham " + s2;

Это не сработает, потому что символьная строка не имеет

operator+
, который принимает
String
в качестве параметра. С другой стороны, если вы определили независимый
operator+
, который принимает два параметра типа
String
, ваш компилятор проверит наличие в классе
String
конструктора, принимающего
const char*
в качестве аргумента (или любой другой тип, который вы используете совместно с
String
), и сконструирует временный объект на этапе выполнения. Поэтому приведенная выше строка эквивалентна следующей.

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

Начальник милиции. Книга 5

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

Папина дочка

Рам Янка
4. Самбисты
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Папина дочка

Возвышение Меркурия. Книга 8

Кронос Александр
8. Меркурий
Фантастика:
героическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Возвышение Меркурия. Книга 8

АллатРа

Новых Анастасия
Научно-образовательная:
психология
история
философия
обществознание
физика
6.25
рейтинг книги
АллатРа

Золушка вне правил

Шах Ольга
Любовные романы:
любовно-фантастические романы
6.83
рейтинг книги
Золушка вне правил

Возвышение Меркурия. Книга 5

Кронос Александр
5. Меркурий
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Возвышение Меркурия. Книга 5

На Ларэде

Кронос Александр
3. Лэрн
Фантастика:
фэнтези
героическая фантастика
стимпанк
5.00
рейтинг книги
На Ларэде

Барон Дубов 6

Карелин Сергей Витальевич
6. Его Дубейшество
Фантастика:
юмористическое фэнтези
аниме
сказочная фантастика
фэнтези
5.00
рейтинг книги
Барон Дубов 6

Ротмистр Гордеев

Дашко Дмитрий Николаевич
1. Ротмистр Гордеев
Фантастика:
фэнтези
попаданцы
альтернативная история
5.00
рейтинг книги
Ротмистр Гордеев

Кодекс Крови. Книга VI

Борзых М.
6. РОС: Кодекс Крови
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Крови. Книга VI

Лучше подавать холодным

Аберкромби Джо
4. Земной круг. Первый Закон
Фантастика:
фэнтези
8.45
рейтинг книги
Лучше подавать холодным

Звездная Кровь. Изгой

Елисеев Алексей Станиславович
1. Звездная Кровь. Изгой
Фантастика:
боевая фантастика
попаданцы
рпг
5.00
рейтинг книги
Звездная Кровь. Изгой

Крестоносец

Ланцов Михаил Алексеевич
7. Помещик
Фантастика:
героическая фантастика
попаданцы
альтернативная история
5.00
рейтинг книги
Крестоносец

Сердце Дракона. Том 12

Клеванский Кирилл Сергеевич
12. Сердце дракона
Фантастика:
фэнтези
героическая фантастика
боевая фантастика
7.29
рейтинг книги
Сердце Дракона. Том 12