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

ЖАНРЫ

QT 4: программирование GUI на С++

Саммерфилд Марк

Шрифт:

В конце концов, заменяя в списке параметров Point2D на const Point2D &, мы уменьшаем накладные расходы на вызов функции — вместо копирования 256 битов (размер четырех типов double) копируются только 64 или 128 бит, что зависит от размера указателя, принятого в целевой платформе.

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

void transpose(Point2D &point)

{

double oldX = point.x;

point.setX(point.y);

point.setY(oldX);

}

В

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

Point2D point;

Point2D &ref = point;

Point2D *ptr = &ref;

Для преобразования указателя в ссылку используется унарный оператор *:

Point2D point;

Point2D *ptr = &point;

Point2D &ref = *ptr;

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

Массивы

Массивы в С++ объявляются с указанием количества элементов массива в квадратных скобках после имени переменной массива. Допускаются двумерные массивы, т.е. массив массивов. Ниже приводится определение одномерного массива, содержащего 10 элементов типа int:

int fibonacci[10];

Доступ к элементам осуществляется с помощью следующей записи: fibonacci[0], fibonacci[1], … fibonacci[9]. Часто требуется инициализировать массив при его определении:

int fibonacci[10] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 };

В таких случаях можно не указывать размер массива, поскольку компилятор может его рассчитать по количеству элементов в списке инициализации:

int fibonacci[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 };

Статическая инициализация также работает для сложных типов, например для Point2D:

Point2D triangle[] = {

Point2D(0.0, 0.0), Point2D(1.0, 0.0), Point2D(0.5, 0.866)

};

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

const int fibonacci[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 };

Для нахождения количества элементов в массиве можно использовать оператор sizeof:

int n = sizeof(fibonacci) / sizeof(fibonacci[0]);

Оператор sizeof возвращает размер аргумента в байтах. Количество элементов массива равно его размеру в байтах, поделенному на размер одного его элемента. Поскольку это долго вводить, распространенной альтернативой является объявление константы и ее использование при

определении массива:

enum { NFibonacci = 10 };

const int fibonacci[NFibonacci] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 };

Есть соблазн объявить константу как переменную типа const int. К сожалению, некоторые компиляторы имеют проблемы при использовании константных переменных для представления размера массива. Ключевое слово enum будет объяснено далее в этом приложении.

Проход в цикле по массиву обычно выполняется с использованием переменной целого типа. Например:

for (int i = 0; i < NFibonacci; ++i)

cout << fibonacci[i] << endl;

Массив можно также проходить с помощью указателя:

const int *ptr = &fibonacci[0];

while (ptr != &fibonacci[10]) {

cout << *ptr << endl;

++ptr;

}

Мы инициализируем указатель адресом первого элемента и проходим его в цикле, пока не достигнем элемента «после последнего элемента» («одиннадцатого» элемента, fibonacci[10]). На каждом шаге цикла оператор ++ продвигает указатель к следующему элементу.

Вместо &fibonacci[0] можно было бы также написать fibonacci. Это объясняется тем, что указанное без элементов имя массива автоматически преобразуется в указатель на первый элемент массива. Аналогично можно было бы подставить fibonacci + 10 вместо &fibonacci[10]. Эти приемы работают и в других местах: мы можем получить содержимое текущего элемента, используя запись *ptr или ptr[0], а получить доступ к следующему элементу могли бы, используя *(ptr + 1) или ptr[1]. Это свойство иногда называют «эквивалентностью указателей и массивов».

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

01 #include <iostream>

02 using namespace std;

03 void printIntegerTable(const int *table, int size)

04 {

05 for (int i = 0; i < size; ++i)

06 cout << table[i] << endl;

07 }

08 int main

09 {

10 const int fibonacci[10] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 };

11 printIntegerTable(fibonacci, 10);

12 return 0;

13 }

Ирония в том, что, хотя С++ не позволяет выбирать между передачей массива по ссылке и передачей по значению, он предоставляет некоторую свободу синтаксиса при объявлении типа параметра. Вместо const int *table можно было бы также написать const int table[] для объявления в качестве параметра указателя на константный тип int. Аналогично параметр argv функции main можно объявлять как char *argv[] или как char **argv.

Поделиться с друзьями: