Главная Обратная связь

Дисциплины:

Архитектура (936)
Биология (6393)
География (744)
История (25)
Компьютеры (1497)
Кулинария (2184)
Культура (3938)
Литература (5778)
Математика (5918)
Медицина (9278)
Механика (2776)
Образование (13883)
Политика (26404)
Правоведение (321)
Психология (56518)
Религия (1833)
Социология (23400)
Спорт (2350)
Строительство (17942)
Технология (5741)
Транспорт (14634)
Физика (1043)
Философия (440)
Финансы (17336)
Химия (4931)
Экология (6055)
Экономика (9200)
Электроника (7621)






Создание производного класса



Важнейшим свойством объектно-ориентированного программирования является наследование. Для того, чтобы показать, что класс Внаследует класс A(класс B выведен из класса A), в определении класса Bпосле имени класса ставится двоеточие и затем перечисляются классы, из которых Bнаследует:

class A{public: A(); ~A(); MethodA();};class B : public A {public: B(); . . .};

Термин "наследование" означает, что класс B обладает всеми свойствами класса A, он их унаследовал. У объекта производного класса есть все атрибуты и методы базового класса. Разумеется, новый класс может добавить собственные атрибуты и методы.

B b;b.MethodA(); // вызов метода базового класса

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



Рис. 1. Пример иерархии классов.

Иерархия классов может быть сколь угодно глубокой. Если нужно различить, о каком именно классе идет речь, класс C называют непосредственным или прямым базовым классом класса D, а класс A – косвенным базовым классом класса D.

 

 

98.Список инициализации для конструктора. Деструктор.

Конструктор предназначен для инициализации объекта и вызывается автоматически при его создании. Ниже перечислены основные свойства конструкторов.

· Конструктор не возвращает значения, даже типа void. Нельзя получить указатель на конструктор.

· Класс может иметь несколько конструкторов с разными параметрами для разных видов инициализации (при этом используется механизм перегрузки).

· Конструктор, который можно вызвать без параметров, называется конструктором по умолчанию.

· Параметры конструктора могут иметь любой тип, кроме этого же класса. Можно задавать значения параметров по умолчанию. Их может содержать только один из конструкторов.

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

· Конструкторы не наследуются.

· Конструктор не может быть константным, статическим и виртуальным (нельзя использовать модификаторы const, virtual и static).



· Конструкторы глобальных объектов вызываются до вызова функции main. Локальные объекты создаются, как только становится активной область их действия. Конструктор запускается и при создании временного объекта (например, при передаче объекта из функции).

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

Конструкторы часто вызываются неявно для создания временных объектов. Обычно это происходит в следующих случаях:

· при инициализации;

· при выполнении операции присваивания;

· для задания значений параметров по умолчанию;

· при создании и инициализации массива;

· при создании динамических объектов;

· при передаче параметров в функцию и возврате результатов по значению.

Деструктор - это особый вид метода, применяющийся для освобождения памяти, занимаемой объектом. Деструктор вызывается автоматически, когда объект выходит из области видимости:

· для локальных переменных - при выходе из блока, в котором они объявлены;

· для глобальных - как часть процедуры выхода из main;

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

При уничтожении массива деструктор вызывается для каждого элемента удаляемого массива. Для динамических объектов деструктор вызывается при уничтожении объекта операцией delete. При выполнении операции delete[] деструктор вызывается для каждого элемента удаляемого массива.



Имя деструктора начинается с тильды (~), непосредственно за которой следует имя класса. Деструктор:

· не имеет аргументов и возвращаемого значения;

· не может быть объявлен как const или static;

· не наследуется;

· может быть виртуальным;

· может вызываться явным образом путем указания полностью уточненного имени; это необходимо для объектов, которым с помощью new выделялся конкретный адрес.

Если деструктор явным образом не определен, компилятор создает его автоматически.

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

Деструктор для рассматриваемого примера должен выглядеть так :

monster::~monster() {delete [] name;}

 

99.Ссылки (type &).

В языке программирования C++ ссылка — это простой ссылочный тип, менее мощный, но более безопасный, чем указатель, унаследованый от языка Си. Название C++ ссылка может приводить к путанице, так как в информатике под ссылкой понимается обобщенный концептуальный тип, а указатели и С++ ссылки являются специфическими реализациями ссылочного типа.

 

Синтаксис и терминология

Объявление вида:

<Type> & <Name>

где <Type> — тип и <Name> — идентификатор, указывают идентификатор, чьим типом является ссылка на <Type>.

Примеры:

1. int A = 5;int& rA = A;extern int& rB;

2. int& foo ();void bar (int& rP);

3. class MyClass { int& m_b; /* ... */ };

4. int funcX() { return 42 ; }; int (&xFunc)() = funcX;

Здесь, rA и rB являются типами «ссылок на int», foo() — функция, возвращающая ссылку на int, bar() — функция с ссылкой в качестве параметра, которая ссылается на int, MyClass — класс (class) с членом, ссылающимся на int, funcX() — функция, возвращающая int, xFunc() — псевдоним для funcX.

Типы, относящиеся к «ссылка на <Type>», иногда называются ссылочными типами. Идентификаторы ссылочного типа называются ссылочными переменными. Вызывать их переменную фактически будет неправильно (показано дальше).

Применение ссылок

· Помимо удобной замены указателям, еще одним полезным применением ссылок являются списки параметров функции, при помощи которых они могут передавать параметры, используемые для вывода без явного взятия адреса вызывающим. Например:

void square(int x, int& result) {

result = x * x;

}

Тогда следующий вызов поместит 9 в y:square(3, y);

Тем не менее, следующий вызов приведет к ошибке компиляции, так как параметры ссылки, не помеченные const, могут только быть адресуемыми значениями:

square(3, 6);

· Возврат ссылки также позволяет непредусмотренный синтаксис, в котором вызовы функции могут быть присвоены:

int& preinc(int& x) { ++x; return x; }

preinc(y) = 5; // то же, что и ++y, y = 5

· Во многих реализациях, обычные механизмы передачи параметров часто подразумевают весьма полезную операцию копирования больших параметров. Ссылки, помеченные const, полезны в качестве способа передачи больших объектов между функциями без накладных расходов:

void f_slow(BigObject x) { /* ... */ }

void f_fast(const BigObject& x) { /* ... */ }

BigObject y; f_slow(y); // медленно, копирует y в параметр x

f_fast(y); // быстро, дает прямой доступ к y (только для чтения)

Если f_slow() действительно требует собственной копии x для модификации, то она должна создать копию явным образом. Тогда как тот же способ может быть применен с использованием указателей, что вызовет модификацию каждого вызова функции, добавив громоздкий оператор взятия по адресу (&) в качестве аргумента, причем довольно сложно будет отменить изменения, если объект становится меньше.

 

 

100.Стандартные типы данных. Преобразование типов данных.

Все программы обрабатывают какую-то информацию. В языках C/C++ данные представляются одним из восьми базовых типов: char (текстовые данные), int (целые числа), float (числа с плавающей запятой одинарной точности), double (числа с плавающей запятой двойной точности), void (пустые значения), bool(логические значения), перечисления и указатели. Остановимся на каждом из типов данных.
• Текст (тип данных char) представляет собой последовательность символов, таких как a, Z, ? иЗ,которые могут быть разделены пробелами. Обычно каждый символ занимает 8 бит, или один байт, с диапазоном значений от 0 до 255.
• Целые числа (тип данных int) находятся в диапазоне от -32768 до 32767 и занимают 16 бит, т.е. два байта, или одно слово. В Windows 98 и WindowsNTиспользуются 32- разрядные целые, что позволяет расширить диапазон их значений от -2147483648 до 2147483647.
• Числа с плавающей запятой одинарной точности (тип данных float) могут представляться как в фиксированном формате, например число л (3,14159), так и в экспоненциальном (7,56310). Диапазон значений — +/-3,4Е-38—3,4Е+38, размерность — 32 бита, т.е. 4 байта, или 2 слова.
• Числа с плавающей запятой двойной точности (тип данных double) имеют диапазон значений от +/-1,7Е-308 до +/-1,7Е+308 и размерность 64 бита, т.е. 8 байтов, или 4 слова. Ранее существовал тип longdouble с размерностью 80 бит и диапазоном от +/- 1,18Е-4932 до +/-1Д8Е+4932. В новых 32-разрядных версиях компиляторов он эквивалентен типу double и поддерживается из соображений обратной совместимости с написанными ранее приложениями.
• Перечисления представляются конечным набором именованных констант различных типов.
• Тип данных void, как правило, применяется в функциях, не возвращающих никакого значения. Этот тип данных также можно использовать для создания обобщенных указателей, как будет показано в главе "Указатели".
• Указатели, в отличие от переменных других типов, не содержат данных в обычном понимании этого слова. Вместо этого указатели содержат адреса памяти, где хранятся данные.
• Переменные нового типа данных bool в C++ могут содержать только одну из двух констант: true или false.

До сих пор в рассмотренных примерах мы в отдельно взятых выражениях использовали переменные только одного типа данных, например intили float.
Но часто бывают случаи, когда в операции участвуют переменные разных типов. Такие oперации называются смешанными. В отличие от многих других языков программиpования, языки С и C++ могут автоматически преобразовывать данные из одного ипа в другой.
Данные разных типов по-разному сохраняются в памяти. Возьмем число 10. Формат, в котором оно будет хранится, зависит от назначенного этому числу типа данных.
Другими словами, сочетание нулей и единиц в представлении одного и того же числа 10 будет разным в зависимости о того, интерпретируется ли оно как целое или как число с плавающей запятой. Давайте рассмотрим следующее выражение, где для двух переменных, fresultifvalue, задан тип данных float, а для переменной ivalue — тип int:
Iresult = fvalue * ivalue;
Это типичный пример смешанной операции, в процессе которой компилятор штоматически выполняет определенные преобразования. Целочисленное значение переменной ivalue будет прочитано из памяти, приведено к типу с плавающей запятой, умножено на исходное значение переменной fvalue, и результат, также в шде значения с плавающей запятой, будет сохранен в переменной fresult. Обратите шимание на то, что значение переменной iivalue при этом никак не меняется, ставаясь целочисленным.
Автоматические преобразования типов данных при выполнении смешанных операций производятся в соответствии с иерархией преобразований. Суть состоит в гом, что с целью повышения производительности в смешанных операциях значения эазных типов временно приводятся к тому типу данных, который имеет больший приоритет в иерархии. Ниже перечислены типы данных в порядке уменьшения приоритета:
double
float
long
int
short
Если значение преобразуется к типу, имеющему большую размерность, потери информации не происходит, вследствие чего не страдает точность вычислений. Посмотрим, что происходит в случае приведения значения типа int к типу float. Предположим, переменные ivaluel и ivalue2 имеют тип int, а переменные fvalue и fresult — тип float. Выполним следующие операции:
ivaluel = 3;
ivalue2 = 4;
fvalue = 7.0;
fresult = fvalue + ivaluel/ivalue2;

Выражение ivaluel/ivalue2 не является смешанной операцией: это деление двух целых чисел, результатом которого будет ноль, поскольку дробная часть (в данном случае 0,75) отбрасывается. Таким образом, переменной fresult будет присвоено значение 7,0. Как изменится результат, если переменная ivalue2 будет описана как float? В таком случае операция ivaluel/ivalue2 станет смешанной. Компилятор автоматически приведет значение переменной ivaluel к типу float — 3,0, и результат деления будет 0,75.
В сумме с переменной fvalue получим 7,75. Важно помнить, что тип переменной, находящейся слева от оператора присваивания, предопределяет тип результата вычислений. Предположим, что переменные fx и fy описаны как float, а переменная iresuit — как int. Рассмотрим следующий фрагмент:
fx = 7.0;
fy = 2.0;
iresult = 4.0+ fx/fy;

Результатом выполнения операции fx/fy будет 3,5. Можно предположить, что переменной iresult будет присвоена сумма 3,5 + 4,0 = 7,5. Но, поскольку это целочисленная переменная, компилятор преобразует число 7,5 в целое, отбрасывая дробную часть. Полученное значение 7 и присваивается переменной iresult.

 

101.Статические члены класса. Друзья _______класса.

Вы можете определить член класса как статический (static member) или член экземпляра (instance member). По умолчанию каждый член определен как член экземпляра. Это значит, что для каждого экземпляра класса делается своя копия этого члена. Когда член объявлен как статический, имеется лишь одна его копия. Статический член создается при загрузке содержащего класс приложения и существует в течение жизни приложения. Поэтому вы можете обращаться к члену, даже если экземпляр класса еще не создан.

Функции-друзья (дружественные функции) — это функции, не являющиеся функциями-членами но тем не менее имеющие доступ к защищённым и закрытым полям и функциям-членам класса. Прототипы таких функций в теле определения должны всегда записываться с ключевым словом friend. Например:

class Matrix {

...

friend Matrix& multiply(Matrix m1, Matrix m2);

...

};

 

Matrix& multiply(Matrix m1, Matrix m2)

{

/*

* алгоритм перемножения матриц m1 и m2

*/

};

 

Здесь multiply и есть та функция - дружественная функция (функция-друг), - котороя может обращаться к любым полям и функциям-членам класса Matrix.

 

Возможно опеределять также и классы-друзья (дружественные классы). Если класс A — друг класса B, то все его функции-члены могут обращаться к любым полям и функциям членам класса B. Например:

class Matrix {

...

friend class Vector;

...

};

где Vector и есть клас-друг класса Matrix; Однако в C++ не действует правило «друг моего друга — мой друг».

По стандартy C++ вложенный класс не имеет прав доступа к закрытым членам объемлющего класса и не может быть объявлен его другом (последнее следует из определения термина друг как нечлена класса). Тем не менее, многие широко распространённые компиляторы нарушают оба эти правила (возможно, ввиду совокупной странности этих правил).

 

 

102.Статические элементы класса.

При создании объектов, с одной стороны, каждый объект имеет свои собственные независимые поля данных, с другой – все объекты одного класса используют одни и те же методы. Методы класса создаются и размещаются в па-мяти компьютера всего один раз – при создании класса, так как нет никакого смысла держать в памяти копии методов для каждого объекта, поскольку у всех объектов методы одинаковые. А поскольку наборы значений полей у каждого объекта свои, поля объектов не должны быть общими. Однако существует ряд ситуаций, когда необходимо, чтобы все представители одного класса включали в себя какое-либо одинаковое значение. Для этих целей служат статические элементы класса.

Статический элемент класса может рассматриваться как глобальная переменная или функция, доступная только в пределах области класса. Для определения статических полей и методов используется ключевое слово static.

Статический элемент данных разделяется всеми представителями данного класса. То есть существует только один экземпляр переменной независимо от числа созданных представителей. Память под статический элемент выделяется, даже если не существует никаких представителей класса.

Определение статических полей класса происходит не так, как для обычных полей. Обычные поля объявляются (компилятору сообщается имя и тип поля) и определяются (компилятор выделяет память для хранения поля) при помощи одного оператора. Для статических полей эти два действия выполняются двумя разными операторами: объявление поля находится внутри определения класса, а определение, как правило, располагается вне класса и зачастую представляет собой определение глобальной переменной. Если бы определение статического поля класса находилось внутри класса (как в ранних версиях C++), то это нарушило бы принцип, в соответствии с которым определение класса не должно быть связано с выделением памяти. Поместив определение статического поля вне класса, мы обеспечили однократное выделение памяти под это поле, до того как программа будет запущена на выполнение, и статиче-ское поле в этом случае будет доступным всему классу.

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

Помимо статических полей, класс может иметь и статические функции. Доступ к статическим функциям может быть осуществлен просто через имя класса, без создания представителя класса, то есть статические элементы-функции класса не ассоциируются с отдельными представителями класса. Другими словами, при вызове им не передается указатель this. Из этого следует, что:

• статическая функция-элемент может вызываться независимо от то-го, существует или нет какой-либо представитель класса;

• статическая функция-элемент может обращаться только к статическим элементам данных класса и вызывать только другие статические функции-элементы класса;

• статическая функция-элемент не может объявляться как virtual.

 

 

103.Cтруктуры - это составной объект, в который входят элементы любых типов, за исключением функций. В отличие от массива, который является однородным объектом, структура может быть неоднородной. Тип структуры определяется записью вида:

struct { список определений }

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

тип-данных описатель;

где тип-данных указывает тип структуры для объектов, определяемых в описателях. В простейшей форме описатели представляют собой идентификаторы или массивы.

Пример:

struct { double x,y; } s1, s2, sm[9];

struct { int year;

char moth, day; } date1, date2;

Переменные s1, s2 определяются как структуры, каждая из которых состоит из двух компонент х и у. Переменная sm определяется как массив из девяти структур. Каждая из двух переменных date1, date2 состоит из трех компонентов year, moth, day. >p>Существует и другой способ ассоциирования имени с типом структуры, он основан на использовании тега структуры. Тег структуры аналогичен тегу перечислимого типа. Тег структуры определяется следующим образом:

struct тег { список описаний; };

где тег является идентификатором.

В приведенном ниже примере идентификатор student описывается как тег структуры:

struct student { char name[25];

int id, age;

char prp; };

Тег структуры используется для последующего объявления структур данного вида в форме:

struct тег список-идентификаторов;

Пример:

struct studeut st1,st2;

Использование тегов структуры необходимо для описания рекурсивных структур. Ниже рассматривается использование рекурсивных тегов структуры.

struct node { int data;

struct node * next; } st1_node;

Тег структуры node действительно является рекурсивным, так как он используется в своем собственном описании, т.е. в формализации указателя next. Структуры не могут быть прямо рекурсивными, т.е. структура node не может содержать компоненту, являющуюся структурой node, но любая структура может иметь компоненту, являющуюся указателем на свой тип, как и сделано в приведенном примере.

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

st1.name="Иванов";

st2.id=st1.id;

st1_node.data=st1.age;

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

Метки операторов формируют отдельный класс имен. Каждая метка оператора должна быть отлична от всех других меток операторов в той же самой функции. Метки операторов

могут совпадать с именами меток других функций. Пример:

struct student

char student[20];

int class;

int id;

student;

В этом примере имя тега структуры, элемента структуры и

переменной относятся к трем различным классам имен, поэтому

не будет противоречия между тремя объектами с именем student.

Компилятор определит по контексту программы как

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

studentпоявится после ключевого слова struct, это будет означать,

что появился tagструктуры. Когда имя studentпоявится после

операции выбора элемента _>или ., то это означает, что имя

ссылается на элемент структуры. В другом контексте имя student

является переменной структурного типа.

 

 

104.Cтруктуры - это составной объект, в который входят элементы любых типов, за исключением функций. В отличие от массива, который является однородным объектом, структура может быть неоднородной. Тип структуры определяется записью вида:

struct { список определений }

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

тип-данных описатель;

где тип-данных указывает тип структуры для объектов, определяемых в описателях. В простейшей форме описатели представляют собой идентификаторы или массивы.

Пример:

struct { double x,y; } s1, s2, sm[9];

struct { int year;

char moth, day; } date1, date2;

Переменные s1, s2 определяются как структуры, каждая из которых состоит из двух компонент х и у. Переменная sm определяется как массив из девяти структур. Каждая из двух переменных date1, date2 состоит из трех компонентов year, moth, day. >p>Существует и другой способ ассоциирования имени с типом структуры, он основан на использовании тега структуры. Тег структуры аналогичен тегу перечислимого типа. Тег структуры определяется следующим образом:

struct тег { список описаний; };

где тег является идентификатором.

В приведенном ниже примере идентификатор student описывается как тег структуры:

struct student { char name[25];

int id, age;

char prp; };

Тег структуры используется для последующего объявления структур данного вида в форме:

struct тег список-идентификаторов;

Пример:

struct studeut st1,st2;

Использование тегов структуры необходимо для описания рекурсивных структур. Ниже рассматривается использование рекурсивных тегов структуры.

struct node { int data;

struct node * next; } st1_node;

Тег структуры node действительно является рекурсивным, так как он используется в своем собственном описании, т.е. в формализации указателя next. Структуры не могут быть прямо рекурсивными, т.е. структура node не может содержать компоненту, являющуюся структурой node, но любая структура может иметь компоненту, являющуюся указателем на свой тип, как и сделано в приведенном примере.

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

st1.name="Иванов";

st2.id=st1.id;

st1_node.data=st1.age;

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

Метки операторов формируют отдельный класс имен. Каждая метка оператора должна быть отлична от всех других меток операторов в той же самой функции. Метки операторов

могут совпадать с именами меток других функций. Пример:

struct student

char student[20];

int class;

int id;

student;

В этом примере имя тега структуры, элемента структуры и

переменной относятся к трем различным классам имен, поэтому

не будет противоречия между тремя объектами с именем student.

Компилятор определит по контексту программы как

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

studentпоявится после ключевого слова struct, это будет означать,

что появился tagструктуры. Когда имя studentпоявится после

операции выбора элемента _>или ., то это означает, что имя

ссылается на элемент структуры. В другом контексте имя student

является переменной структурного типа.

 

 


Эта страница нарушает авторские права

allrefrs.ru - 2019 год. Все права принадлежат их авторам!