07 Point2D::Point2D(double x, double у)
08 {
09 xVal = x;
10 yVal = у;
11 }
12 void Point2D::setX(double x)
13 {
14 xVal = x;
15 }
16 void Point2D::setY(double у)
17 {
18 yVal = у;
19 }
20 double Point2D::x const
21 {
22 return xVal;
23 }
24 double Point2D::y const
25 {
26 return yVal;
27 }
Этот файл начинается с включения заголовочного файла point2d.h, потому что прежде чем компилятор будет выполнять синтаксический анализ реализаций функций—членов, он должен иметь определение класса. Затем идут реализации функций, перед именем которых через оператор :: указывается имя класса.
Мы узнали, как можно реализовать встроенную функцию и как можно реализовать ее в файле .cpp. Семантически эти два подхода эквивалентны, однако при вызове встроенной функции большинство компиляторов просто разворачивают тело функции вместо формирования реального вызова функции. Обычно это ведет к получению более быстрого кода, но может увеличить размер приложения. По этой причине только очень короткие функции следует делать встроенными; длинные функции всегда следует реализовывать в файле .cpp. Кроме того, если мы забудем реализовать какую-нибудь функцию и попытаемся ее вызвать, компоновщик «пожалуется» на существование неразрешенного символа.
Теперь попытаемся использовать этот класс.
01 #include "point2d.h"
02 int main
03 {
04 Point2D alpha;
05 Point2D beta(0.666, 0.875);
06 alpha.setX(beta.y);
07 beta.setY(alpha.x);
08 return 0;
09 }
В С++ переменные любого типа можно объявлять без непосредственного использования оператора new. Первая переменная инициализируется с помощью стандартного конструктора Point2D (т.е. конструктора без параметров). Вторая переменная инициализируется с использованием второго конструктора. Обращение к члену объекта осуществляется с использованием оператора . (точка).
Объявленные таким образом переменные ведут себя как элементарные типы Java и C# (такие, как int и double). Например, при использовании оператора присваивания копируется содержимое переменной, а не ссылка на объект. И если позже переменная будет модифицирована, значение всех других переменных, к которым присваивалась первая переменная, не изменится.
С++, как объектно—ориентированный язык, поддерживает наследование и полиморфизм. Для иллюстрации этих свойств мы рассмотрим пример абстрактного класса Shape (фигура) и подкласса Circle (окружность). Начнем с базового класса:
01 #ifndef SHAPE_H
02 #define SHAPE_H
03 #include "point2d.h"
04 class Shape
05 {
06 public:
07 Shape(Point2D center) { myCenter = center; }
08 virtual void draw = 0;
09 protected:
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 }