Объединения, встраиваемые функции

Наследование

Наследование - это процесс, посредством которого один объект может приобретать свойства другого. Точнее, объект может наследовать основные свойства другого объекта и добавлять к ним черты, характерные только для него. Наследование является важным, поскольку оно позволяет поддерживать концепцию иерархии классов. Применение иерархии классов делает управляемыми большие потоки информации. Когда один класс наследуется другим, класс который наследуется, называется базовым классом. Наследующий класс называется производным классом. Обычно процесс наследования начинается с задания базового класса. Базовый класс определяет все те качества, которые будут общими для всех производных классов. Рассмотрим пример:

class base { // Задание базового класса

int i;

public:

void set_i(int n);

int get_i();

};

class derived: public base { // Задание производного класса

int j;

public:

void set_j(int n);

int mul();

};

void base::set_i(int n)

{ i = n; } // Установка значения i в базовом классе

int base::get_i()

{ return i; } // Возврат значения i в базовом классе

void derived::set_j(int n)

{ j = n; } // Установка значения j в производном классе

int derived::mul()

{ return j * get_i(); } // Вызов значения i из base и, одновременно, j из derived

main()

{

derived ob;

ob.set_i(10); // загрузка i в base

ob.set_j(4); // загрузка j в derived

cout << ob.mul(); // вывод числа 40

return 0;

}

Отметим, что функция get_i(), которая является членом базового класса, а не производного, вызывается внутри производного класса без какой бы - то ни было связи с каким-либо объектом. Это возможно потому, что открытые члены класса base становятся открытыми членами derived.

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

Остановимся на важном понятии как объединение, которое наряду с классами и структурами часто используется при программировании на С++, кроме того оно очень наглядно демонстрирует мощь объектно-ориентированного программирования. В С++ объединение также определяет тип класса, в котором функции и данные могут содержаться в виде членов. Объединение похоже на структуру тем, что в нем, по умолчанию, все члены открыты до тех пор, пока не используется спецификатор private.

Главное же в том, что в объединении в С++ все данные - члены находятся в одной области памяти. Объединения могут содержать конструкторы и деструкторы. Способность объединений связывать воедино программу и данные позволяет создавать типы данных, в которых все данные используют общую память. Это именно то, чего нельзя сделать, используя классы. Имеется несколько ограничений, накладываемых на использование объединений применительно к С++. Во-первых, они не могут наследовать какой бы то ни было класс, и они не могут использоваться в качестве базового класса для любого другого типа. Объединения не должны содержать объектов с конструктором и деструктором. Рассмотрим пример декларирования объединения в С++:

# include <iostream.h>

union bits

{

bits(double n);

void show_bits();

double d;

}

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

Рассмотрим применение и назначение встраиваемых функций. В С++ можно задать функцию, которая, фактически, не вызывается, а ее тело встраивается в программу в месте ее вызова. Преимуществом встраиваемых функций является то, что они не связаны с вызовом функций и механизмом возврата. Это значит, что встраиваемые функции могут выполняться гораздо быстрее обычных. Недостатком встраиваемых функций является то, что если они слишком большие и вызываются слишком часто, объем использующих их программ сильно возрастает. Из-за этого применение встраиваемых функций обычно ограничивается короткими функциями. Для определения встраиваемой функции вписывается спецификатор inline перед определением функции. Рассмотрим сказанное на примере:

# include <iostream.h>

inline int even(int x)

{

return! (x%2);

}

main()

{

int n;

cin >>n;

if (even(n)) cout << ‘’ число является четным \n’’;

else cout << ‘’ число является нечетным \n’’;

return 0;

}

В этом примере функция even(), которая возвращает истину при четном аргументе, объявлена как встраиваемая функция. Это означает, что строка

if (even(n)) cout << ‘’ число является четным \n’’:

функционально идентична строке

if (! (10%2)) cout << ‘’ число является четным \n’’;

Этот пример указывает также на другую важную особенность использования встраиваемой функции: она должна быть задана до ее первого вызова. Отметим, что спецификатор inline является запросом, а не командой для компилятора. Очень важно подчеркнуть и то, что некоторые компиляторы не воспринимают функцию как встраиваемую, если она содержит статическую переменную (static), оператор цикла, оператор switch или go to или, если функция является рекурсивной. Если определение функции - члена достаточно короткое, это определение можно включить в объявление класса. Поступив таким образом, мы заставляем, если это возможно, функцию стать встраиваемой. При этом ключевое слово inline не используется. Рассмотрим сказанное на примере:

# include <iostream.h>

class samp

{

int i, j;

public:

samp(int a, int b);

int divisible () { return! (i % j);} // определение встраиваемой функции

}

samp:: samp (int a, int b)

{

i = a;

j = b;

}

main

{

samp ob1 (10,2), ob2 (10,3);

if (ob1.devisible()) cout << ‘’ Число делится нацело \n’’;

else cout << ‘’ Число не делится нацело \n’’;

if (ob2.devisible()) cout << ‘’ Число делится нацело \n’’;

else cout << ‘’ Число не делится нацело \n’’;

return 0;

}

Особым видом структур данных является объединение. Определение объединения напоминает определение структуры, только вместо ключевого слова struct используется union:

union number { short sx; long lx; double dx;};

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

union number { short sx; // если type равен ShortType long lx; // если type равен LongType double dx; // если type равен DoubleType };

Как показано на примере память выделяется только для максимального из этих трех атрибутов (в данном случае dx). Однако и обращаться с объединением надо осторожно. Поскольку все три атрибута делят одну и ту же область памяти, изменение одного из них означает изменение всех остальных. Объединение располагает все три своих атрибута по одному и тому же адресу.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: