В языке С++ класс стал расширением понятия структуры, он задает некоторый структурный тип данных (данные-члены класса) и набор операций над ними (функции-члены класса или методы).
Тип данных класс можно определить с помощью конструкции
ключ_класса имя_класса { список _членов };
Здесь ключ_класса - одно из служебных слов struct, union, class;
имя_класса - произвольный идентификатор;
список_членов - определения и описания членов класса, как данных так и функций.
Класс - это набор из одной или более переменных и функций, возможно, различных типов, сгруппированных под одним именем.
Пример структуры - учётная карточка сотрудника, в которой содержится Ф.И.О., адрес, должность, год поступления на работу и т.д. Некоторые из этих атрибутов сами могут оказаться структурами. Так, Ф.И.О. имеет три компоненты, адрес - также несколько компонент.
Каждый из данных-членов класса распознаётся по своему имени, которое должно быть уникальным в данном классе. Члены класса часто называются его элементами или полями.
Хотя каждому имени поля класса сопоставлен определённый тип, самостоятельным объектом такой элемент не является. Память выделяется только для конкретного объекта класса в целом.
Введем новые типы FIO и sotrudnik:
struct FIO
{ char *familia, // указатели на строки с фамилией,
*imya, // именем и отчеством
*otchestvo;
};
struct sotrudnik
{ FIO name; // ФИО сотрудника ( три указателя )
char *dol; // должность ( указатель на строку)
float okl; // оклад (сумма оклада)
};
Заметим, что наличие ";" после фигурных скобок здесь обязательно.
Теперь можно объявить структурные переменные типа FIO или sotrudnik, то 6есть выделить память для них, также как это делается для обычных переменных:
FIO chel1, chel2, chel3;
sotrudnik s1, s2, s[50];
Компилятором будет выделена память под переменные chel1, chel2, chel3 (типа FIO), а также s1, s2 и под массив s из пятидесяти структур (типа sotrudnik). Отметим, что число байтов, выделяемое под структурную переменную, не всегда равно сумме длин отдельных элементов структуры из-за эффекта выравнивания, производимого компилятором. Чтобы определить реально выделенное число байтов, надо воспользоваться операцией sizeof, например, так:
int nf=sizeof (FIO), ns=sizeof (sotrudnik);
После того, как определены структурные переменные, доступ к их элементам осуществляется с помощью операции извлечения '.':
Имена наподобие chel1.familia, с помощью которых можно получить доступ к элементам класса, называют уточненными именами.
Рассмотрим работу с использованием указателей, которую можно разделить на три этапа :
· создание указателя : FIO *fio_p;
· установка указателя на объект : fio_p = &chel3;
· работа с объектом : обратиться к отдельному элементу структуры chel3 можно двумя способами:
(*fio_p).familia
fio_p -> familia
Первый и второй этапы можно объединить : FIO *fio_p = &chel3;
Обычно для работы с данными класса создают специальные методы, такой подход позволяет скрыть подробности работы с данными – они остаются внутри класса, во внешней же вызывающей программе видны лишь вызовы методов и передаваемые параметры.
Добавим в класс два метода для обращения к данным :
· define() будет заполнять данные-члены класса
· display() будет отображать их на экране.
#include <iostream.h>
struct FIO
{ char *familia,
*imya,
*otchestvo;
};
// определение класса sotrudnik
struct sotrudnik
{ FIO name;
char *dol;
float okl;
// запись данных класса
void define(FIO fnam, char *pd, float ok)
{ name.familia = fnam.familia;
name.imya = fnam.imya;
name.otchestvo = fnam.otchestvo;
dol = pd;
okl = ok;
}
void display()
{ cout<<endl;
cout<< name.familia<<" ";
cout<<name.imya;
cout<<" "<<name.otchestvo<<" , ";
cout<<dol<<" , ";
cout<<"оклад -"<<okl;
}
}; // конец определения класса sotrudnik
void main()
{ sotrudnik s1,s2,s3; // выделение памяти под объекты s1,s2,s3
Очень часто требуется вводить данные с клавиатуры, рассмотрим решение этой задачи, выполненное в виде диалога с оператором, расположенного в вызывающей программе – в функции main().
В программе имеются две временные переменные для ввода данных с клавиатуры : str и chel.
Переменная str служит для ввода строки с клавиатуры. В программе необходимо вводить разнообразные строки, например фамилию, должность и т.п. Обратите внимание, что все строки представлены указателями, то есть при создании объекта (экземпляра класса s1 или структуры chel) выделяется память под указатель, память же под строку не выделяется и потому должна быть выделена динамически из кучи в процессе работы программы.
Строка текста, адресуемая через указатель, формируется в три этапа :
· строка вводится с клавиатуры в статический массив str;
· в соответствии с введенной строкой, выделяется память «из кучи», одновременно на неё устанавливается указатель;
· информация из массива str копируется в выделенную область памяти.
Переменная сhel служит для ввода информации типа FIO, состоящей из трех указателей на строки (фамилия, имя, отчество).
Метод define() инициализирует экземпляр класса s1.
#include <iostream.h>
// определение структуры FIO
struct FIO
{ … …};
// определение класса sotrudnik
struct sotrudnik
{ … …};
void main()
{ char str[40],*ps;// массив для ввода строки с клавиатуры
FIO chel; // переменная для ввода Ф.И.О.
sotrudnik s1; // экземпляр класса sotrudnik
// ввод фамилии :
// «приглашение для ввода» и ввод строки с клавиатуры
cout<<"Фамилия :";
cin>>str;
//выделение памяти из кучи, установка указателя chel.familia
chel.familia=new char[strlen(str)+1];
/* копирование строки из массива str в динамическую память, адресуемую указателем chel.familia */
strcpy (chel.familia,str);
// ввод имени
cout<<"Имя :";
cin>>str;
chel.imya=new char[strlen(str)+1];
strcpy (chel.imya,str);
// ввод отчества
cout<<"Отчество :";
cin>>str;
chel.otchestvo=new char[strlen(str)+1];
strcpy (chel.otchestvo,str);
// ввод должности
cout<<"Должность :";
cin>>str;
ps=new char[strlen(str)+1];
strcpy (ps,str);
s1.define(chel,ps,35000);// занесение данных в объект s1
s1.display(); |// вывод данных на экран
}
Обратите внимание каким образом данные заносятся в объект s1 при обращении к методу define(). Адреса строк, находящиеся в переменной chel (в указателях chel.familia, chel.imya, chel.otchestvo), записываются в переменные объекта s1 (в указатели name.familia, name .imya, name.otchestvo), то есть происходит передача указателей на строки, сами же строки, размещенные в динамической памяти остаются на своих местах.
Отметим, что нужно стремиться максимально «разгружать» программный код в вызывающей программе, то есть оформлять максимально возможную часть алгоритма в виде методов класса. Такой подход традиционен в объектно-ориентированном программировании, он делает алгоритм решения более понятным, сокращает количество используемых переменных, дает и другие преимущества.
В следующем варианте программы ввод данных с клавиатуры и заполнение данных класса выполнено в виде метода класса kl_define().
#include <iostream.h>
struct FIO
{ … …};
// определение класса sotrudnik
struct sotrudnik
{ …
kl_define()
{ char str[40];
cout<<endl;
cout<<"Фамилия :";
cin>>str;
name.familia=new char[strlen(str)+1];
strcpy (name.familia,str);
cout<<"Имя :";
cin>>str;
name.imya=new char[strlen(str)+1];
strcpy (name.imya,str);
cout<<"Отчество :";
cin>>str;
name.otchestvo=new char[strlen(str)+1];
strcpy (name.otchestvo,str);
cout<<"Должность :";
cin>>str;
dol=new char[strlen(str)+1];
strcpy (dol,str);
cout<<"Оклад :";
cin>>okl;
}
…
}; // конец определения класса sotrudnik
void main()
{sotrudnik s1; // объявление объекта s1
s1.kl_define(); // ввести данные объекта s1 с клавиатуры