В этой программе целочисленной переменной sb.num присваивается число 15. Перестановка двух байтов, составляющих это значение, выполняется путем обмена двух символов, которые образуют массив ch. В результате старший и младший байты целочисленной переменной num меняются местами. Эта операция возможна лишь потому, что как переменная num, так и массив ch разделяют одну и ту же область памяти.
В следующей программе демонстрируется еще один пример использования объединения. Здесь объединения связываются с битовыми полями, используемыми для отображения в двоичной системе счисления ASCII-кода, генерируемого при нажатии любой клавиши. Эта программа также демонстрирует альтернативный способ отображения отдельных битов, составляющих байт. Объединение позволяет присвоить значение нажатой клавиши символьной переменной, а битовые поля используются для отображения отдельных битов.
// Отображение ASCII-кода символов в двоичной системе счисления.
#include <iostream>
#include <conio.h>
using namespace std;
// Битовые поля, которые будут расшифрованы.
struct byte {
unsigned a : 1;
unsigned b : 1;
unsigned с : 1;
unsigned d : 1;
unsigned e : 1;
unsigned f : 1;
unsigned g : 1;
unsigned h : 1;
};
union bits {
char ch;
struct byte bit;
}ascii;
void disp_bits(bits b);
Int main()
{
do {
cin >> ascii.ch;
cout << ":";
disp_bits(ascii);
}while(ascii.ch!='q'); // Выход при вводе буквы "q".
return 0;
}
// Отображение конфигурации битов для каждого символа.
void disp_bits(bits b)
{
if(b.bit.h) cout << "1";
else cout << "0";
if(b.bit.g) cout << "1";
else cout << "0";
if(b.bit.f) cout << "1";
else cout << "0 ";
if(b.bit.e) cout << "1";
else cout << "0";
if(b.bit.d) cout << "1";
else cout << "0";
if(b.bit.c) cout << "1";
else cout << "0";
if(b.bit.b) cout << "1";
else cout << "0";
if(b.bit.a) cout << "1";
else cout << "0";
cout << "";
}
Вот как выглядит один из возможных вариантов выполнения этой программы.
А: 0 1 1 0 0 0 0 1
B: 0 1 1 0 0 0 1 0
С: 0 1 1 0 0 0 1 1
D: 0 1 1 0 0 1 0 0
Е: 0 1 1 0 0 1 0 1
F: 0 1 1 0 0 1 1 0
G: 0 1 1 0 0 1 1 1
H: 0 1 1 0 1 0 0 0
I: 0 1 1 0 1 0 0 1
J: 0 1 1 0 1 0 1 0
K: 0 1 1 0 1 0 1 1
1: 0 1 1 0 1 1 0 0
M: 0 1 1 0 1 1 0 1
N: 0 1 1 0 1 1 1 0
O: 0 1 1 0 1 1 1 1
P: 0 1 1 1 0 0 0 0
Q: 0 1 1 1 0 0 0 1
Важно! Поскольку объединение предполагает, что несколько переменных разделяют одну и ту же область памяти, это средство предоставляет программисту возможность хранить информацию, которая (в зависимости от ситуации) может содержать различные типы данных, и получать доступ к этой информации. По сути, объединения обеспечивают низкоуровневую поддержку принципов полиморфизма. Другими словами, объединение обеспечивает единый интерфейс для нескольких различных типов данных, воплощая таким образом концепцию "один интерфейс — множество методов" в своей самой простой форме.
Анонимные объединения
Анонимные объединения позволяют объявлять переменные, которые разделяют одну и ту же область памяти.
В C++ предусмотрен специальный тип объединения, который называется анонимным. Анонимное объединение не имеет наименования типа, и поэтому объект такого объединения объявить невозможно. Но анонимное объединение сообщает компилятору о том, что его члены разделяют одну и ту же область памяти. При этом обращение к самим переменным объединения происходит непосредственно, без использования оператора "точка". Рассмотрим такой пример.
//Демонстрация использования анонимного объединения.
#include <iostream>
using namespace std;
Int main()
{
// Это анонимное объединение.
union {
short int count;
char ch[2];
};
// Вот как происходит непосредственное обращение к членам анонимного объединения.
ch[0] = 'X';
ch[1] = 'Y';
cout << "Объединение в виде символов: " << ch[0] <<ch[1] << '';
cout << "Объединение в виде целого значения: " <<count << '';
return 0;
}
Эта программа отображает следующий результат.
Объединение в виде символов: XY
Объединение в виде целого значения: 22872
Число 22872 получено в результате помещения символов X и Y в младший и старший байты переменной count соответственно. Как видите, к обеим переменным, входящим в состав объединения, как count, так и ch, можно получить доступ так же, как к обычным переменным, а не как к составляющим объединения. Несмотря на то что они объявлены как часть анонимного объединения, их имена находятся на том же уровне области видимости, что и другие локальные переменные, объявленные на уровне объединения. Таким образом, член анонимного объединения не может иметь имя, совпадающее с именем любой другой переменной, объявленной в той же области видимости.
Анонимное объединение представляет собой средство, с помощью которого программист может сообщить компилятору о своем намерении, чтобы две (или больше) переменные разделяли одну и ту же область памяти. За исключением этого момента, члены анонимного объединения ведут себя подобно любым другим переменным.
Использование оператора sizeof для гарантии переносимости программного кода
Как было показано, структуры и объединения создают объекты различных размеров, которые зависят от размеров и количества их членов. Более того, размеры таких встроенных типов, как int, могут изменяться при переходе от одного компьютера к другому. Иногда компилятор заполняет структуру или объединение так, чтобы выровнять их по границе четного слова или абзаца. (Абзац содержит 16 байт.) Поэтому, если в программе нужно определить размер (в байтах) структуры или объединения, используйте оператор sizeof. Не пытайтесь вручную выполнять сложение отдельных членов. Из-за заполнения или иных аппаратно-зависимых факторов размер структуры или объединения может оказаться больше суммы размеров отдельных их членов.
И еще. Объединение всегда будет занимать область памяти, достаточную для хранения его самого большого члена. Рассмотрим пример.
union х {
char ch;
int i;
double f;
} u_var;
Здесь при выполнении оператора sizeof u_var получим результат 8 (при условии, что double-значение занимает 8 байт). Во время выполнения программы не имеет значения, что реально будет храниться в переменной u_var; здесь важен размер самой большой переменной, входящей в состав объединения, поскольку объединение должно иметь размер самого большого его элемента.
Переходим к объектно-ориентированному программированию
Эта глава заключает описание не объектно-ориентированных атрибутов C++. Начиная со следующей главы, мы будем рассматривать средства, которые поддерживают объектно-ориентированное программирование (Object Oriented Programming— OOP), или ООП. Чтобы понять объектно-ориентированные средства C++ и научиться их эффективно применять, необходимо глубокое понимание материала этой и предыдущих девяти глав. Поэтому, возможно, вам стоит повторить пройденный материал. Особое внимание при повторении уделите указателям, структурам, функциям и перегрузке функций.
Глава 11: Введение в классы
В этой главе мы познакомимся с классом. Класс — это фундамент, на котором построена С++-поддержка объектно-ориентированного программирования, а также ядро многих более сложных программных средств. Класс — это базовая единица инкапсуляции, которая обеспечивает механизм создания объектов.
Основы понятия класса
Объектно-ориентированное программирование построено на понятии класса.
Начнем с определения терминов класса и объекта. Класс определяет новый тип данных, который задает формат объекта. Класс включает как данные, так и код, предназначенный для выполнения над этими данными. Следовательно, класс связывает данные с кодом. В C++ спецификация класса используется для построения объектов. Объекты — это экземпляры класса. По сути, класс представляет собой набор планов, которые определяют, как строить объект. Важно понимать, что класс — это логическая абстракция, которая реально не существует до тех пор, пока не будет создан объект этого класса, т.е. то, что станет физическим представлением этого класса в памяти компьютера.
Определяя класс, вы объявляете данные, которые он содержит, и код, который выполняется над этими данными. Хотя очень простые классы могут содержать только код или только данные, большинство реальных классов содержат оба компонента. В классе данные объявляются в виде переменных, а код оформляется в виде функций. Функции и переменные, составляющие класс, называются его членами. Таким образом, переменная, объявленная в классе, называется членом данных, а функция, объявленная в классе, называется функцией-членом. Иногда вместо термина член данных используется термин переменная экземпляра (или переменная реализации).
Объявление класса начинается с ключевого слова class.
Класс создается с помощью ключевого слова class. Объявление класса синтаксически подобно объявлению структуры. Рассмотрим пример. Следующий класс определяет тип queue, который предназначен для реализации очереди. (Под очередью понимается список с дисциплиной обслуживания в порядке поступления, т.е. "первым прибыл — первым обслужен".)
// Создание класса queue.
class queue {
int q[100];
int sloc, rloc;
public:
void init();
void qput(int i);
int qget();
};