На этом шаге мы рассмотрим первый пример программы, использующей классы и объекты.
Приведем примеры трех программ, в которых объявлены и использованы простейшие классы.
Программа приведена ниже, результат ее работы - на рисунке 1.
// 74_1.cpp: главный файл проекта. #include "stdafx.h" #include <stdio.h> #include <conio.h> using namespace System; class A { protected: //к этим данным имеют доступ только методы //данного класса и производных int x; int у; public: int a; //"плохое" объявление: члены-данные, int b; //как правило, не описываются в этой секции! int f1(int x, int y) //метод класса { return x-y; } }; class B : A { //это объявляется класс, производный от А //при этом наследуются члены класса А public: int f2(int x) //метод класса { //здесь могут использоваться члены-данные базового //класса из секции protected. Так как имя аргумента //метода f2() совпадает именем поля х из класса А, //унаследованного классом В, то чтобы различить, //какая переменная к какому классу относится, //потребовалось уточнить 'это с помощью спецификатора //::. Показано, что в теле метода х берется тот, что //унаследован от А, и собственный аргумент х самого //метода f2(): A::x = 20; return x + A::x; } }; void main() { A min; //создание экземпляров классов А, В B max; min.a = 10; //Работа с элементами класса А из секции public min.b = 20; int x1 = min.f1(min.a, min.b); int x2 = max.f2(10); // Работа с элементом класса В printf("x1=%d\nx2= %d\n",x1,x2); _getch(); }
Рис.1. Результат работы приложения
Обратите внимание, что когда вы создаете переменную типа класса, т. е. экземпляр класса (например, A min;, где min - экземпляр класса), то для этого экземпляра никакой памяти не выделяете, а компилятор все-таки размещает этот экземпляр где-то в памяти. Почему?
Среда исполнения приложений в C/C++ выделяет два вида памяти: статическую и динамическую. Последняя носит название "куча" (heap). Куча может быть управляемой и неуправляемой. Если компилятор способен определить размер памяти под объявленную переменную, то он выделяет для нее место в статической памяти (например, под переменную типа int он выделит 4 байта, а под массив типа int из 10 элементов он выделит 40 байтов).
Если же компилятор не в состоянии определить размер памяти под объявленную переменную, то он потребует от программиста поместить такую переменную в куче - в динамической памяти. Кстати, если программист захочет работать с динамической памятью (даже когда компилятор может поместить переменную в статической памяти), то язык это ему позволяет. Для простых переменных при этом используются:
Сейчас более современными конструкциями, выполняющими указанные операции являются:
Перечисленные конструкции работают с неуправляемой кучей. С управляемой кучей работает утилита gcnew() и нет необходимости освобождать самому память от объекта, поскольку в этом случае работает так называемая автоматическая сборка мусора: когда объект становится ненужным, память от него освобождается.
Чтобы работать с такой кучей, надо перейти в режим CLR. Что касается вопроса "почему?", то очевидно, что компилятор размещает переменную типа класса, объявленного нами, в статической области памяти.
Но приведем пример этой же программы, в которой используется (по нашему желанию) динамическая память (мы, естественно, приводим только тело функции main (), где это отражается).
void main() { A* min = new A(); //создание указателей на B* max = new B(); //экземпляры классов А,В min->a = 10; //Работа с элементами класса А из секции public min->b = 20; int x1 = min->f1(min->a,min->b); int x2 = max->f2(10); // Работа с элементом класса B printf("x1=%d\nx2= %d\n",x1,x2); _getch(); delete min; //возврат выделенной delete max; //памяти в кучу }
Данном случае операция new размещает объект в куче, выдает адрес этого объекта, а конструктор инициализирует объект.
На следующем шаге мы рассмотрим второй пример программы с классами.