C++. Классы. Часть 1. Понятие класса. Объявление типа данных «класс». Объект класса. Классы в среде CLR. Инкапсуляция данных в классе

C++. Классы. Часть 1. Понятие класса. Объявление класса. Объект класса. Классы в среде CLR. Инкапсуляция данных в классе


Содержание



1. Основные понятия объектно-ориентированного программирования. Классы и объекты

В языке программирования C++ понятие «класс» лежит в основе объектно-ориентированного программирования (ООП). Объектно-ориентированное программирование возникло как усовершенствование процедурно-ориентированного программирования.

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

В основе ООП лежат понятия «объект» и «класс». В языке программирования объект – это переменная типа «класс». Класс описывает данные и методы (функции), которые будут использоваться объектом этого класса. Каждый класс описывает логически-завершенную единицу программы. Инкапсуляция данных и методов их обработки в пределах класса позволяет улучшить структурированность программных систем. Это в свою очередь уменьшает риск возникновения «невидимых» логических ошибок. Использование наследственности и полиморфизма в классах позволяет избежать повторяемости программного кода и удобно упорядочить сложные вызовы методов, объединенных между собой в список.

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

В классе объявляются данные (внутренние переменные, свойства) и методы (функции), которые оперируют этими данными (выполняют работу над данными).

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

Также класс может быть родительским для других классов, которые наследуют его программный код.

2. Какие виды классов языка C++ можно реализовать в среде CLR?

В среде CLR (Common Language Runtime) поддерживаются два вида классов:

  • неуправляемые (unmanaged) классы. Для выделения памяти под объекты таких классов могут использоваться неуправляемые указатели (*) и операция new;
  • управляемые (managed) классы. Для выделения памяти в таких классах могут быть использованы управляемые указатели (^) и операция gcnew.

Данная тема освещает особенности использования unmanaged (*) классов.

Примеры, которые демонстрируют особенности использования и отличие между управляемыми (^) и неуправляемыми (*) классами более подробно описываются в теме:

3. Общая форма объявления unmanaged-класса. Ключевое слово «class»

В простейшем случае (без наследования) общая форма объявления unmanaged-класса имеет следующий вид

class имя_класса
{
    private:
    // скрытые (закрытые) данные и методы (функции)
    // ...

    public:
    // открытые данные и методы
    // ...

    protected:
    // защищенные данные и методы
    // ...
};

где

  • имя_класса – непосредственно имя нового типа данных «класс». Это имя используется при создании объектов класса.

Ключевое слово class сообщает о том, что объявляется новый класс (тип данных). Внутри класса объявляются члены класса: данные и методы. Ключевое слово private определяет члены класса, которые должны быть закрыты от внешних методов, объявленных за пределами класса, а также объектов класса. Члены данных, объявленные с ключевым словом private, доступны только другим членам этого класса.

Ключевое слово public определяет общедоступные данные (переменные) и методы (функции) класса.

Ключевое слово protected определяет защищенные данные и методы класса, которые есть:

  • доступными для методов унаследованных от данного класса;
  • недоступными для методов, реализованных в других частях программы;
  • недоступными для объектов (экземпляров) класса.

В пределах описания класса секции (разделы) private, protected, public могут следовать в любом порядке и в любом количестве. Например:

class MyClass
{
    // секция (раздел) private по умолчанию
    // ...

    public:
    // секция public
    // ...

    protected:
    // секция protected
    // ...

    private:
    // опять секция private
    // ...

    public:
    // опять секция public
    // ...
};

4. Что означает термин «инкапсуляция данных» в классе?

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

Инкапсуляция обеспечивает улучшение надежности сохранения данных в классе путем ввода дополнительных методов проверки этих данных на допустимые значения. Как правило, доступ к скрытым данным в классе происходит не напрямую, а через вызовы специальных методов доступа или свойств класса. Непосредственно данные размещаются в скрытой секции (разделе) класса, а методы доступа к этим данным размещаются в общедоступном разделе класса.

Классический язык C++ позволяет устанавливать доступ к членам класса с помощью трех спецификаторов: private, protected, public.

5. Какие типы доступа могут иметь члены класса? Какие различия между членами класса, объявленными с ключевыми словами private, protected, public?

Члены класса могут иметь три основных типа доступа, которые определяются соответствующими ключевыми словами:

  • private – члены класса есть скрытыми. Это означает, что доступ к ним имеют только методы, которые объявлены в классе. private-члены класса есть недоступными из унаследованных классов и объектов этого класса;
  • protected – члены класса есть защищенными. Это означает, что доступ к protected-членам имеют методы данного класса и методы унаследованных классов. protected-члены класса есть недоступными для объектов этого класса;
  • public – члены класса есть открытыми (доступными) для всех методов и объектов из всех других частей программного кода.

6. Может ли класс, при его объявлении, содержать только данные и не содержать методов?

Класс может быть объявлен без методов. Такие классы содержат только данные. В этом случае они мало чем отличаются от структур. Чтобы получить доступ к данным в классе, не содержащим методов, нужно эти данные объявить в разделе public. Классы без методов почти не применяются. Если объявить данные в разделе private, то получить доступ к членам-данным класса будет невозможно.

Пример. В данном примере объявляется класс без методов, который реализует операции над датой. Класс содержит внутренние переменные (данные), что представляют собой:

  • число;
  • месяц;
  • год.
class CMyDate
{
    public:
    int day; // число
    int month; // месяц
    int year; // год
};

Фрагмент кода, который демонстрирует работу с классом CMyDate

// объявить объект класса MyDate
CMyDate D;

// заполнить значениями поля класса
D.day = 20;
D.month = 02;
D.year = 2002;

int t = D.year; // t = 2002
t = D.month; // t = 2

7. Пример объявления пустого класса

Да, класс может быть объявлен без данных и без методов. Например, ниже объявлен класс, который не содержит ни данных ни методов.

// класс не содержит ни данных ни методов
class NoData
{

};

Объект такого класса также создается.

NoData nd; // объект класса NoData

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

8. Пример класса, содержащего методы (функции)

Основные преимущества классов обнаруживаются при наличии методов – членов класса. С помощью методов доступа к данным в классах можно удобно:

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

Пример. Модификация класса CMyDate. Класс, который описывает дату и операции над ней. Операции доступа к членам класса реализованы с помощью соответствующих методов. Сами данные реализованы в разделе private.

Программный код класса

class CMyDate
{
    int day;
    int month;
    int year;

    public:
    void SetDate(int d, int m, int y); //
    int GetDay(void); // возвращает номер дня
    int GetMonth(void); // возвращает номер месяца
    int GetYear(void); // возвращает год
};

Реализация методов класса SetDate(), GetDay(), GetMonth(), GetYear()

// Установить новую дату
void CMyDate::SetDate(int d, int m, int y)
{
    day = d;
    month = m;
    year = y;
}

// считать номер дня
int CMyDate::GetDay(void)
{
    return day;
}

// считать номер месяца
int CMyDate::GetMonth(void)
{
    return month;
}

// считать год
int CMyDate::GetYear(void)
{
    return year;
}

Использование методов класса из другого программного кода (например, обработчика события в приложениях типа Windows Forms)

// объект класса
CMyDate MD;

// установить значение даты 20.02.2002
MD.SetDate(20, 2, 2002);

// считать дату
int t;
t = MD.GetDay(); // t = 20
t = MD.GetMonth(); // t = 2
t = MD.GetYear(); // t = 2002

9. В каких частях класса и программы можно объявлять реализацию методов класса? Пример

Реализацию методов класса можно объявлять в классе и за пределами класса.

Например. В приведенном ниже программном коде объявляется класс СMyTіме. Класс содержит два метода SetTime1() и SetTime2(), которые выполняют одинаковую работу: устанавливают новое время. Тело (реализация) метода SetTime1() описывается в классе CMyTime. Реализация метода SetTime2() описывается за пределами класса. В классе описывается только прототип (декларация) метода SetTime2().

class CMyTime
{
    private:
    int sec; // секунда
    int min; // минута
    int hour; // час

    public:
    // декларация и реализация метода внутри класса
    void SetTime1(int h, int m, int s)
    {
        // реализация
        hour = h;
        min = m;
        sec = s;
    }

    // только декларация (прототип) метода (метод SetTime2 есть членом класса)
    void SetTime2(int h, int m, int s); // реализация метода - за пределами класса
};

// реализация метода SetTime2() за пределами класса
void CMyTime::SetTime2(int h, int m, int s)
{
    hour = h;
    min = m;
    sec = s;
}

Тело метода, который описывается за пределами класса, может быть описано в другом модулн. Как правило, в системе Microsoft Visual Studio этот модуль имеет расширение *.cpp. Сам же класс описывается в модуле с расширением *.h.

10. Какое назначение имеет оператор расширения области видимости (доступа) ‘::’?

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

11. Что такое объект класса? Какие отличия между объектом класса и объявлением класса? Объявление объекта класса

Объявление класса – это описание формата типа данных. Этот тип данных «класс» описывает данные и методы, которые оперируют этими данными. Описание класса – это только описание (объявление). В этом случае память для класса не выделяется.

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

Объявление объекта класса (экземпляра) ничем не отличается от объявления переменной:

Имя_класса имя_объекта;

С помощью имени имя_объекта можно осуществить доступ к общедоступным (public) членам класса. Это осуществляется с помощью символа ‘ . ‘ (точка).

Возможен также вариант объявления указателя на класс. Если это unmanaged-класс, то объявление имеет вид:

Имя_класса * имя_объекта;

После такого объявления, нужно выделять память для объекта класса с помощью оператора new. Доступ к данным по указателю осуществляется с помощью комбинации символов ‘->’ точно так же как и в случае со структурами.

Например. Объявление класса Worker, описывающего методы и данные о работнике предприятия.

class Worker
{
    private:
    // private-члены класса Worker
    // ...

    public:
    // public-члены класса Worker
    // ...
};

Объект класса – это переменная типа «класс». При объявлении объекта класса выделяется память для этого объекта (переменной). Например, для класса Worker можно написать следующий код

Worker w1; // объект класса Worker, выделяется память для данных объекта

Из объекта можно иметь доступ только к public-членам класса. Это можно осуществлять с помощью символа ‘ . ‘ (точка) или доступа по указателю ‘->’:

w1.SetName("Johnson J."); // вызов public-метода
Worker * w2; // указатель на класс Worker
w2 = new Worker(); // динамическое выделение памяти для класса
w2->SetName("Jackson M."); // вызов public-метода из класса по указателю

12. Какой тип доступа по умолчанию имеют члены класса в C++?

По умолчанию, члены класса имеют доступ private. Поэтому, при объявлении класса, если нужно указать private-члены класса, это слово можно опустить.

Например, пусть заданы объявления класса, который описывает пиксель на экране монитора.

class CMyPixel
{
    int color; // раздел private
    int x;
    int y;

    public:
    // общедоступные методы (раздел public)
    void SetXYColor(int nx, int ny, int ncolor)
    {
        x = nx; y = ny; color = ncolor;
    }

    int GetX(void) { return x; }
    int GetY(void) { return y; }
    int GetColor(void) { return color; }
};

По всей видимости, в начале объявления класса, раздел private отсутствует. Это означает, что члены-данные класса color, x, y есть скрытыми. При создании объекта класса и прямом доступе к ним компилятор выдаст ошибку

CMyPixel MP; // объявить объект класса
// MP.x = 25; - ошибка - "CMyPixel::x - cannot access private member declared in CMyPixel
MP.SetXYColor(25, 15, 2); // так правильно

13. Каким образом можно реализовать доступ к private-членам класса?

Как правило, private-члены класса есть закрытыми. Это есть основное преимущество инкапсуляции. Чтобы изменять значения private-членов класса, используют методы класса, которые объявлены в public-секции. В этих методах можно изменять значения private-членов. Такой подход используется для обеспечения надежности сохранения данных в private-членах. В public-методах, которые имеют доступ к private-членам, можно реализовать дополнительные проверки на допустимость значений.

Например.

Пусть дан класс, который определяет массив из n вещественных чисел. Класс содержит два скрытых (private) члена данных:

  • n — количество элементов массива;
  • A — непосредственно массив.

Судя из логики задачи, в таком классе количество элементов массива не может быть меньше нуля (<0). Поэтому, в методе SetN(), который устанавливает значение n, целесообразно проводить проверку на корректность значения n.

Программный код класса, который демонстрирует доступ к private-членам класса приведен ниже.

class CMyArray
{
    int n;
    float *A; // массив типа float, память для массива выделяется динамически

    public:
    CMyArray(); // конструктор класса, без него никак
    void SetN(int nn); // установить новое n, обнулить массив A
    int GetN(void); // считать n
    void SetAi(int index, float value); // установить новое значение в A[index]
    float GetAi(int index); // считать значение с A[index]
};

// конструктор класса, без него никак
CMyArray::CMyArray()
{
    n = 0;
}

// установить новое значение n, и установить в 0 значение элементов массива
void CMyArray::SetN(int nn)
{
    if (n>0) delete A; // освободить память, выделенную предварительно для массива A
    n = nn;
    A = new float[n]; // выделить память для n элементов массива

    // заполнить значениями 0 элементы массива
    for (int i=0; i<n; i++)
        A[i] = 0;
}

// считать n
int CMyArray::GetN(void)
{
    return n;
}

// установить новое значение в A[index]
void CMyArray::SetAi(int index, float value)
{
    if (n<=0) return;
    if (index > n-1) return;
    A[index] = value;
}

// считать значение A[index]
float CMyArray::GetAi(int index)
{
    return (float)A[index];
}

Как видно из программного кода, в классе объявляется новый элемент — конструктор класса. Это специальный метод, который используется для начальной инициализации членов данных класса. Более подробно о конструкторах и деструкторах класса описывается в темах:

Фрагмент использования класса CMyArray из другого программного кода:

CMyArray MA; // создать объект класса CMyArray
int d;
double x;

MA.SetN(15);
d = MA.GetN(); // d = 15

MA.SetAi(3, 2.88);
x = MA.GetAi(3); // x = 2.88
x = MA.GetAi(10); // x = 0

MA.Set(20); // переопределение массива
d = MA.GetN(); // d = 20
x = MA.GetAi(3); // x = 0


Связанные темы