Категории
Самые читаемые
Лучшие книги » Компьютеры и Интернет » Программирование » Язык программирования C++. Пятое издание - Стенли Липпман

Язык программирования C++. Пятое издание - Стенли Липпман

Читать онлайн Язык программирования C++. Пятое издание - Стенли Липпман

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 70 71 72 73 74 75 76 77 78 ... 297
Перейти на страницу:

const int *cp = &i; // ok: но cp не может изменить i (раздел 2.4.2)

const int &r = i;   // ok: но r не может изменить i (раздел 2.4.1)

const int &r2 = 42; // ok: (раздел 2.4.1)

int *p = cp;  // ошибка: типы p и cp не совпадают (раздел 2.4.2)

int &r3 = r;  // ошибка: типы r3 и r не совпадают (раздел 2.4.1)

int &r4 = 42; // ошибка: нельзя инициализировать простую ссылку из

              //         литерала (раздел 2.3.1)

Те же правила инициализации относятся и к передаче параметров:

int i = 0;

const int ci = i;

string::size_type ctr = 0;

reset(&i);  // вызывает версию функции reset с параметром типа int*

reset(&ci); // ошибка: нельзя инициализировать int* из указателя на

            //         объект const int

reset(i);   // вызывает версию функции reset с параметром типа int&

reset(ci);  // ошибка: нельзя привязать простую ссылку к константному

            //         объекту ci

reset(42);  // ошибка: нельзя привязать простую ссылку к литералу

reset(ctr); // ошибка: типы не совпадают; ctr имеет беззнаковый тип

// ok: первый параметр find_char является ссылкой на константу

find_char("Hello World!", 'o', ctr);

Ссылочную версию функции reset() (см. раздел 6.2.2) можно вызвать только для объектов типа int. Нельзя передать литерал, выражение, результат которого будет иметь тип int, объект, который требует преобразования, или объект типа const int. Точно так же версии функции reset() с указателем можно передать только объект типа int* (см. раздел 6.2.1). С другой стороны, можно передать строковый литерал как первый аргумент функции find_char() (см. раздел 6.2.2). Ссылочный параметр этой функции — ссылка на константу, и можно инициализировать ссылки на константу из литералов.

По возможности используйте ссылки на константы

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

В качестве примера рассмотрим функцию find_char() из раздела 6.2.2. Строковый параметр этой функции правильно сделан ссылкой на константу. Если бы этот параметр был определен как string&:

// ошибка: первый параметр должен быть const string&

string::size_type find_char(string &s, char c,

                            string::size_type &occurs);

то вызвать ее можно было бы только для объекта класса string, так что

find_char("Hello World", 'o', ctr);

привело бы к неудаче во времени компиляции.

Более того, эту версию функции find_char() нельзя использовать из других функций, которые правильно определяют свои параметры как ссылки на константу. Например, мы могли бы использовать функцию find_char() в функции, которая определяет, является ли строка предложением:

bool is_sentence(const string &s) {

 // если в конце s есть точка, то строка s - предложение

 string::size_type ctr = 0;

 return find_char(s, ctr) == s.size() - 1 && ctr == 1;

}

Если бы функция find_char() получала простую ссылку string?, то этот ее вызов привел бы к ошибке при компиляции. Проблема в том, что s — ссылка на const string, но функция find_char() была неправильно определена как получающая простую ссылку.

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

Правильный способ решения этой проблемы — исправить параметр функции find_char(). Если невозможно изменить функцию find_char(), определите локальную копию строки s в функции is_sentence() и передавайте эту строку функции find_char().

Упражнения раздела 6.2.3

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

bool is_empty(string& s) { return s.empty(); }

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

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

(a) Функция compare() возвращает значение типа bool и получает два параметра, являющиеся ссылками на класс matrix.

(b) Функция change_val() возвращает итератор vector<int> и получает два параметра: один типа int, а второй итератор для вектора vector<int>.

Упражнение 6.19. С учетом следующего объявления определите, какие вызовы допустимы, а какие нет. Объясните, почему они недопустимы.

double calc(double);

int count(const string &, char);

int sum(vector<int>::iterator, vector<int>::iterator, int);

vector<int> vec(10);

(a) calc(23.4, 55.1); (b) count("abcda", 'a');

(c) calc(66);         (d) sum(vec.begin(), vec.end(), 3.8);

Упражнение 6.20. Когда ссылочные параметры должны быть ссылками на константу? Что будет, если сделать параметр простой ссылкой, когда это могла быть ссылка на константу?

6.2.4. Параметры в виде массива

Массивы обладают двумя особенностями, влияющими на определение и использование функций, работающих с массивами: массив нельзя скопировать (см. раздел 3.5.1), имя массива при использовании автоматически преобразуется в указатель на его первый элемент (см. раздел 3.5.3). Поскольку копировать массив нельзя, его нельзя передать функции по значению. Так как имя массива автоматически преобразуется в указатель, при передаче массива функции фактически передается указатель на его первый элемент.

Хотя передать массив по значению нельзя, вполне можно написать параметр, который выглядит как массив:

// несмотря на внешний вид,

// эти три объявления функции print эквивалентны

// у каждой функции есть один параметр типа const int*

void print(const int*);

void print(const int[]);   // демонстрация намерения получить массив

void print(const int[10]); // размерность только для документирования

Независимо от внешнего вида, эти объявления эквивалентны: в каждом объявлена функция с одним параметром типа const int*. Когда компилятор проверяет вызов функции print(), он выясняет только то, что типом аргумента является const int*:

int i = 0, j[2] = {0, 1};

print(&i); // ok: &i - int*

print(j);  // ok: j преобразуется в int*, указывающий на j[0]

Если передать массив функции print(), то этот аргумент автоматически преобразуется в указатель на первый элемент в массиве; размер массива не имеет значения.

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

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

Использование маркера для определения продолжения массива

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

void print(const char *cp) {

 if (cp)      // если cp не нулевой указатель

  while (*cp) // пока указываемый символ не является нулевым

   cout << *cp++; // вывести символ и перевести указатель

}

1 ... 70 71 72 73 74 75 76 77 78 ... 297
Перейти на страницу:
На этой странице вы можете бесплатно скачать Язык программирования C++. Пятое издание - Стенли Липпман торрент бесплатно.
Комментарии