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

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

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

10 Point2D myCenter;

11 };

12 #endif

Определение класса создается в заголовочном файле с именем shape.h. Поскольку в этом определении делается ссылка на класс Point2D, мы включаем заголовочный файл point2d.h.

Класс Shape не имеет базового класса. В отличие от Java и C#, в С++ не предусмотрен обобщенный класс Object, который наследуется всеми другими классами. Qt предоставляет QObject в качестве естественного базового класса для объектов всех типов.

Объявление функции draw() имеет две интересные особенности. Она содержит ключевое слово virtual и завершается равенством = 0. Ключевое слово virtual означает, что данная функция может быть переопределена в подклассах. Подобно C# функции—члены в С++ по умолчанию не могут переопределяться. Странное приравнивание = 0 указывает на то, что данная функция — чисто виртуальная функция, которая не имеет реализации по умолчанию, и она должна быть реализована в подклассах. Концепция «интерфейса» в Java и C# соответствует в С++ классу, содержащему только чисто виртуальные функции.

Ниже приводится определение подкласса Circle:

01 #ifndef CIRCLE_H

02 #define CIRCLE_H

03 #include "shape.h"

04 class Circle : public Shape

05 {

06 public:

07 Circle(Point2D center, double radius = 0.5)

08 : Shape(center) {

09 myRadius = radius;

10 }

11 void draw() {

12 // здесь выполняются какие-то действия

13 }

14 private:

15 double myRadius;

16 };

17 #endif

Класс Circle наследует класс Shape в открытой форме, т.е. все открытые члены класса Shape остаются открытыми в Circle. С++ поддерживает также защищенное и закрытое наследование, которое ограничивает доступ к открытым и защищенным членам базового класса.

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

Circle(Point2D center, double radius = 0.5)

: Shape(center), myRadius(radius) { }

С другой стороны, С++ не позволяет инициализировать переменную—член в определении класса, поэтому следующий программный код неверен:

// НЕ БУДЕТ КОМПИЛИРОВАТЬСЯ

private:

double myRadius = 0.5;

};

Сигнатура функции draw() совпадает с сигнатурой виртуальной функции draw(), определенной в классе Shape. Она здесь переопределяется и будет вызываться полиморфно, когда draw() вызывается экземпляром Circle через ссылку или указатель на Shape. С++ не имеет ключевого слова override, доступного в C#. С++ также не имеет ключевых слов super и base, ссылающихся на базовый класс. Если требуется вызвать базовую реализацию функции, можно перед именем функции указать имя базового класса и оператор ::. Например:

01 class LabeledCircle : public Circle

02 {

03 public:

04 void draw() {

05 Circle::draw();

06 drawLabel();

07 }

08 };

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

class DerivedClass : public BaseClass1, public BaseClass2, …,

public BaseClassN

{

};

По умолчанию функции и переменные, объявленные в классе, связываются с экземплярами этого класса. Мы можем объявлять статические функции—члены и статические переменные—члены, которые могут использоваться без экземпляра. Например:

01 #ifndef TRUCK_H

02 #define TRUCK_H

03 class Truck

04 {

05 public:

06 Truck() { ++counter; }

07 ~Truck() { --counter; }

08 static int instanceCount() { return counter; }

09 private:

10 static int counter;

11 };

12 #endif

Статическая переменная—член счетчика counter отслеживает количество экземпляров truck, которые существуют в любой момент времени. Конструктор truck его увеличивает на единицу. Деструктор, опознаваемый по префиксу ~, уменьшает счетчик на единицу. В С++ деструктор автоматически вызывается, когда статически распределенная переменная выходит из области видимости или когда удаляется переменная, память для которой выделяется при помощи оператора new. Это аналогично тому, что делается в методе finalize() в Java, за исключением того, что мы можем рассчитывать на его вызов в определенный момент времени.

Статическая переменная—член существует в единственном экземпляре для класса — такие переменные являются «переменными класса», а не «переменными экземпляра». Каждая статическая переменная—член должна определяться в файле .cpp (но без повторения ключевого слова static). Например:

#include "truck.h"

int Truck::counter = 0;

Если этого не сделать, компоновщик выдаст сообщение об ошибке из-за наличия «неразрешенного символа». Обращаться к статической функции instanceCount() можно за пределами класса, указывая имя класса перед ее именем. Например:

01 #include <iostream>

02 #include "truck.h"

03 using namespace std;

04 int main()

05 {

06 Truck truck1;

07 Truck truck2;

08 cout << Truck::instanceCount() << " equals 2" << endl;

09 return 0;

10 }

Указатели

Указатель в С++ — это переменная, содержащая не сам объект, а адрес памяти, где располагается объект. Java и C# имеют аналогичную концепцию «ссылки» при другом синтаксисе. Мы начнем с рассмотрения придуманного нами примера, иллюстрирующего применение указателей:

01 #include "point2d.h"

02 int main()

03 {

04 Point2D alpha;

05 Point2D beta;

06 Point2D *ptr;

07 ptr = &alpha;

08 ptr->setX(1.0);

09 ptr->setY(2.5);

10 ptr = &beta;

11 ptr->setX(4.0);

12 ptr->setY(4.5);

13 ptr = 0;

14 return 0;

15 }

В этом примере используется класс Point2D из предыдущего подраздела. В строках 4 и 5 определяется два объекта типа Point2D. Эти объекты инициализируются в значение (0, 0) стандартным конструктором Point2D.

В строке 6 определяется указатель на объект Point2D. Для обозначения указателя здесь используется звездочка перед именем переменной. Поскольку мы не инициализируем указатель, он будет содержать произвольный адрес памяти. Эта ситуация изменяется в строке 7, в которой адрес alpha присваивается этому указателю. Унарный оператор & возвращает адрес памяти, где располагается объект. Адрес обычно представляет собой 32-битовое или 64-битовое целое число, задающее смещение объекта в памяти.

В строках 8 и 9 мы обращаемся к объекту alpha с помощью указателя ptr. Поскольку ptr является указателем, а не объектом, необходимо использовать оператор -> (стрелка) вместо оператора . (точка).

В строке 10 указателю присваивается адрес beta. С этого момента любая выполняемая нами операция с этим указателем будет воздействовать на объект beta.

В строке 13 указатель устанавливается в нулевое значение. С++ не имеет ключевого слова для представления указателя, который не ссылается ни на один объект; вместо этого мы используем значение 0 (или символическую константу NULL, которая разворачивается в 0). Попытка применения нулевого указателя приведет к краху приложения с выводом такого сообщения об ошибке, как «Segmentation fault» (ошибка сегментации), «General protection fault» (общая ошибка защиты) или «Bus error» (ошибка шины). Применяя отладчик, можно найти строку программного кода, которая приводит к краху.

В конце функции объект alpha содержит пару координат (1.0, 2.5), а объект beta — (4.0,4.5).

Указатели часто используются для хранения объектов, память для которых выделяется динамически с помощью оператора new. Используя жаргон С++ можно сказать, что эти объекты распределяются в «куче», в то время как локальные переменные (т.е. переменные, определенные внутри функции) хранятся в «стеке».

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