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

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

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

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

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

double calc(double);

int count(const string &, char);

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

vector 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. Когда ссылочные параметры должны быть ссылками на константу? Что будет, если сделать параметр простой ссылкой, когда это могла быть ссылка на константу?

<p>6.2.4. Параметры в виде массива</p>

Массивы обладают двумя особенностями, влияющими на определение и использование функций, работающих с массивами: массив нельзя скопировать (см. раздел 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++; // вывести символ и перевести указатель

}

Это соглашение хорошо работает с данными, где есть очевидное значение конечного маркера (такое, как нулевой символ), который не встречается в обычных данных. Это работает значительно хуже с такими данными, как целые числа, где каждое значение в диапазоне вполне допустимо.

Использование соглашения стандартной библиотеки
Перейти на страницу:

Похожие книги