KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » Жасмин Бланшет - QT 4: программирование GUI на С++

Жасмин Бланшет - QT 4: программирование GUI на С++

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Жасмин Бланшет, "QT 4: программирование GUI на С++" бесплатно, без регистрации.
Перейти на страницу:

int i = 5;

int j = 10;

i = j;

• Типы ссылок. Это такие классы, как Integer (в Java), String и MyVeryOwnClass. Их экземпляры создаются при помощи оператора new. Оператор присваивания копирует только ссылку на объект, а для действительного копирования объекта мы должны вызывать функцию clone() (в Java) или Clone() (в C#). Например:

Integer i = new Integer(5);

Integer j = new Integer(10);

i = j.clone();

В С++ все типы могут использоваться как «типы ссылок», а в дополнение к этому те из них, которые допускают копирование, могут использоваться как «типы значений». Например, в С++ нет необходимости иметь класс, подобный Integer, потому что можно использовать указатели и оператор new:

int *i = new int(5);

int *j = new int(10);

*i = *j;

В отличие от Java и C#, в С++ определяемые пользователем типы используются так же, как встроенные типы:

Point2D *i = new Point2D(5, 5);

Point2D *j = new Point2D(10, 10);

*i = *j;

Если требуется сделать класс С++ копируемым, необходимо предусмотреть в этом классе конструктор копирования и оператор присваивания. Конструктор копирования вызывается при инициализации объекта другим объектом того же типа. Синтаксически в С++ это обеспечивается двумя способами:

Point2D i(20, 20);

Point2D j(i); // первый способ

Point2D k = i; // второй способ

Оператор присваивания вызывается при присваивании одной переменной другой переменной:

Point2D i(5, 5);

Point2D j(10, 10);

j = i;

При определении класса компилятор С++ автоматически обеспечивает конструктор копирования и оператор присваивания, выполняющие копирование члена в член. Для класса Point2D это равносильно тому, как если бы мы написали следующий программный код в определении класса:

01 class Point2D

02 {

03 public:

04 Point2D(const Point2D &other)

05 : xVal(other.xVal), yVal(other.yVal) { }

06 Point2D &operator=(const Point2D &other)

07 {

08 xVal = other.xVal;

09 yVal = other.yVal;

10 return *this;

11 }

12 …

13 private:

14 double xVal;

15 double yVal;

16 };

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

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

class BankAccount

{

public:

private:

BankAccount(const BankAccount &other);

BankAccount &operator=(const BankAccount &other);

};

В Qt многие классы проектировались как используемые по значению. Они имеют конструктор копирования и оператор присваивания и обычно инстанцируются в стеке без использования оператора new. Это относится к классам QDateTime, QImage, QString и к классам—контейнерам, например QList<T>, QVector<T> и QMap<K, T>.

Другие классы попадают в категорию «типа ссылок», в частности QObject и его подклассы (QWidget, QTimer, QTcpSocket и т.д.). Они имеют виртуальные функции и не могут копироваться. Например, QWidget представляет конкретное окно или элемент управления на экране дисплея. Если в памяти находится 75 экземпляров QWidget, на экране также будет находиться 75 окон или элементов управления. Обычно эти классы инстанцируются при помощи оператора new.

Глобальные переменные и функции

С++ позволяет объявлять функции и переменные, которые не принадлежат никакому классу и к которым можно обращаться из любой другой функции. Мы видели несколько примеров глобальных функций, в частности main() — точка входа в программу. Глобальные переменные встречаются реже, потому что они плохо влияют на модульность и реентерабельность. Все же важно иметь представление о них, поскольку вам, возможно, придется с ними столкнуться в программном коде, написанном программистом, который раньше писал на С, и другими пользователями С++.

Для иллюстрации работы глобальных функций и переменных рассмотрим небольшую программу, которая печатает список из 128 псевдослучайных чисел, используя придуманный на скорую руку алгоритм. Исходный код программы находится в двух файлах .cpp.

Первый исходный файл — random.cpp:

01 int randomNumbers[128];

02 static int seed = 42;

03 static int nextRandomNumber()

04 {

05 seed = 1009 + (seed * 2011);

06 return seed;

07 }

08 void populateRandomArray()

09 {

10 for (int i = 0; i < 128; ++i)

11 randomNumbers[i] = nextRandomNumber();

12 }

В этом файле объявляются две глобальные переменные (randomNumbers и seed) и две глобальные функции (nextRandomNumber() и populateRandomArray()). В двух объявлениях используется ключевое слово static; эти объявления видимы только внутри текущей единицы компиляции (random.cpp), и говорят, что они статически связаны (static linkage). Два других объявления доступны из любой единицы компиляции программы, они обеспечивают внешнюю связь (external linkage).

Статическая компоновка идеально подходит для вспомогательных функций и внутренних переменных, которые не должны использоваться в других единицах компиляции. Она снижает риск «столкновения» идентификаторов (наличия глобальных переменных с одинаковым именем или глобальных функций с одинаковой сигнатурой в разных единицах компиляции) и не позволяет злонамеренным или другим опрометчивым пользователям получать доступ к внутренним объектам единицы компиляции.

Теперь рассмотрим второй файл main.cpp, в котором используется две глобальные переменные, объявленные в random.cpp с обеспечением внешней связи:

01 #include <iostream>

02 using namespace std;

03 extern int randomNumbers[128];

04 void populateRandomArray();

05 int main()

06 {

07 populateRandomArray();

08 for (int i = 0; i < 128; ++i)

09 cout << randomNumbers[i] << endl;

10 return 0;

11 }

Мы объявляем внешние переменные и функции до их вызова. Объявление randomNumbers внешней переменной (что делает ее видимой в текущей единице компиляции) начинается с ключевого слова extern. Если бы не было этого ключевого слова, компилятор «посчитал» бы, что он имеет дело с определением переменной, и компоновщик «пожаловался» бы на определение одной и той же переменной в двух единицах компиляции (random.cpp и main.cpp). Переменные могут объявляться любое количество раз, однако они могут иметь только одно определение. Именно благодаря определению компилятор резервирует пространство для переменной.

Функция populateRandomArray() объявляется с использованием прототипа. Указывать ключевое слово extern для функций необязательно.

Обычно объявления внешних переменных и функций помещают в заголовочный файл и включают его во все файлы, где они требуются:

01 #ifndef RANDOM_H

02 #define RANDOM_H

03 extern int randomNumbers[128];

04 void populateRandomArray();

05 #endif

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

01 void nextPrime()

02 {

03 static int n = 1;

04 do {

05 ++n;

06 } while (!isPrime(n));

07 return n;

08 }

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

Пространства имен

Пространства имен позволяют снизить риск конфликта имен в программах С++. Конфликты имен часто возникают в больших программах, использующих несколько библиотек независимых разработчиков. В своей собственной программе вы решаете сами, использовать ли вам или нет пространства имен.

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*