Существует два вида преобразований: Явное и неявное.
1.Присвоение "большему типу" значения "меньшего типа". Безопасное присвоение, гарантирует сохранение значения.
unsigned int UnsignedIntVal;
unsigned char UnsignedCharVal;
UnsignedIntVal = UnsignedCharVal;
2.Присвоение "меньшему типу" значения "большего типа". Потенциально опасное присвоение, грозит потерей информации.
int IntVal;
char CharVal;
CharVal = IntVal;
3.Преобразование значения из "меньшего типа" в "больший тип". Называется расширением типа.
(unsigned int)UnsignedCharVal;
4.Преобразование значения из "большего типа" в "меньший тип". Называется сужением типа. Является опасным преобразованием.
(char)IntVal;
Корректное выполнение действий со значениями различных типов в безопасных случаях и в ряде опасных случаев обеспечивается благодаря реализованной в C++ системе преобразования типов.
При трансляции выражений с различными типами операндов транслятор использует механизмы неявных преобразований, которые основываются на следующих правилах стандартных преобразований:
- Присваивание значения объекту преобразует это значение к типу объекта.
· unsigned int MyIntU;
· MyIntU = 3.14159;
· Эквивалентно
· MyIntU = (unsigned int)3.14159;
- Передача значения при вызове функции преобразует это значение в тип параметра функции. Он становится известен благодаря прототипу вызываемой функции.
· void ff(int); // Прототип функции.
·:::::
· ff(3.14159);
· Эквивалентно
· ff((int)3.14159);
При этом на стадии трансляции возможно появление предупреждения о сужении типа.
- В арифметическом выражении тип результата выражения определяется самым "широким" типом среди всех образующих выражение операндов. Этот тип называют результирующим типом выражения. К этому типу преобразуются все остальные операнды.
· unsigned int MyIntU = 5;
|
· …(MyIntU + 3.14159)…
Результирующим типом выражения здесь оказывается тип double, представленный в выражении литералом 3.14159. В процессе вычисления выражения значение переменной MyIntU преобразуется в 5.0, к которому прибавляется 3.14159.
- Преобразование типа при вычислениях арифметических выражений применяется к копиям значений образующих выражение подвыражений. В процессе преобразования типов результаты преобразований подвыражениям не присваиваются.
· unsigned int MyIntU = 5;
· MyIntU = MyIntU + 3.14159;
Здесь имеют место два последовательных преобразования:
По ходу вычисления выражения значение переменной MyIntU расширяется до double и к расширенной копии значения 5.0 прибавляется 3.14159. После этого результирующее значение 8.14159, в соответствии с первым правилом, сужается до типа unsigned int. В результате чего получается значение 8, которое и присваивается переменной MyIntU.
- Указатель на любой не являющийся константой тип можно присваивать указателю типа void*. Этот указатель способен адресовать объекты любого типа данных. Он используется всякий раз, когда неизвестен тип объекта.
· int iVal;
· int *p_iVal = 0;
· char *p_chVal = 0;
· void *p_Val;
· const int *pc_iVal = &iVal;
· p_Val = p_iVal;
· p_Val = p_chVal;
· // ПРАВИЛО 5 выполняется...
· p_Val = pc_iVal;
· //Ошибка: pc_iVal - указатель на константу.
· const void *pcVal = pc_iVal;
· /*
· А здесь всё хорошо! Указателю на константу присвоен указатель на константу.
· */
Перед операцией разыменования указатель типа void* нужно явно преобразовать в указатель на конкретный тип, поскольку в этом случае отсутствует информация о типе, подсказывающая транслятору способ интерпретации битовой последовательности, представляемой указателем:
|
char *p_chValName = "Marina";
p_Val = p_chValName;
p_chVal = (char*)p_Val; /*Явное приведение.*/
Механизм неявных преобразований может быть отключён посредством явного указания в тексте программы требуемого преобразования типов.
Так, модификация ранее рассмотренного примера
MyIntU = MyIntU + (int)3.14159;
отключает механизм неявных преобразований и при вычислении значения переменной производится лишь одно преобразование типа, которое заключается в сужении типа значения литерала 3.14159.
*Тупо копия. Описать самому… бред.
Тут надо понять все это.
Вопрос равносильный «Что такое begin end?»
Классы, использующие свободную память: определение и реализация, использование экземпляров класса, возникающие проблемы. Копирующий конструктор и деструктор, перегрузка оператора присваивания: определение и использование.
// Не важно, какой тип будет использовать свободную память, юзается она new не
// зависимо от того класс это, структура или что-то другое.
////////////////
Оператор C++ new позволяет вашим программам распределять память во время выполнения. Для использования оператора new вам необходимо указать количество байтов памяти, которое требуется программе. Предположим, например, что вашей программе необходим 50-байтный массив. Используя оператор new, вы можете заказать эту память, как показано ниже:
char *buffer = new char[50];
Говоря кратко, если оператор new успешно выделяет память, он возвращает указатель на начало области этой памяти.
|
Зачем необходимо динамически распределять память с использованием new
Многие программы интенсивно используют массивы для хранения множества значений определенного типа. При проектировании своих программ программисты обычно пытаются объявить массивы с размерами, достаточными для удовлетворения будущих потребностей программы. К сожалению, если случится так, что потребности программы когда-нибудь превысят подобные ожидания программиста, то кому-то придется редактировать и перекомпилировать такую программу.
Конструктор – это метод класса, выполняющийся автоматически в момент создания объекта.
class _class
{
private:
char data;
public:
class(char d):data(d) {} //конструктор (инициализирующий)
};
int main
{
int tt=666;
_class* _example = new _class(tt); //создание в свободной памятиэкземпляра класса
cout << *_example -> data << endl; //выведем значение на экран
};
Копирующий конструктор – конструктор, копирующий один объект в другой, производится почленным копированием каждого элемента объекта.
class _class
{
private:
char data;
public:
_class(_class& _example) { //копирующий конструктор
data = _example.data;
}
};
Если вашей программе больше не нужна выделенная память, она должна ее освободить, используя оператор delete. Для освобождения памяти с использованием оператора delete вы просто указываете этому оператору указатель на данную область памяти, как показано ниже:
delete pointer;
Деструктор представляет собой функцию, которую C++ автоматически запускает, когда он или ваша программа уничтожает объект. Деструктор имеет такое же имя, как и класс объекта; однако вы предваряете имя деструктора символом тильды (~), например ~_class. В своей программе вы определяете деструктор точно так же, как и любой другой метод класса.
class _class
{
private:
char data;
public:
class(char d):data(d) {}
~_class (void) {} //указывает деструктор
};
Оператор присваивания
Оператор присваивания обязательно определяется в виде функции класса, потому что он неразрывно связан с объектом, находящимся слева от "=". Определение оператора присваивания в глобальном виде сделало бы возможным переопределение стандартного поведения оператора "=". Пример:
class _class
{
private:
int data;
public:
_class(int i): data(i){}
_class& operator=(const _class& right) { //проверка на самоприсваивание
if (this == &right) {
return *this;
}
data = right.data; //если не равно само себе, то …
return *this;
}
};
Типы отношений между классами. Контейнерные классы: определение, видимость членов класса. Реализация и вызов конструкторов и деструкторов вложенных классов. Реализация и использование методов.
//////////////////
//В шпорах написано, что всего два типа
// Спасибо поржал…
///////////////////////
В традиционном ООП предусмотрены три типа отношений между классами:
- Использование: непосредственная зависимость.
- член класса А отправляет сообщение объекту класса Б
///////// или
- член класса А создает или возвращает объекты класса Б.
- Включение: иногда называется агрегированием. Реализует логические связи типа «является составной частью».
- объект класса А содержит внутренние объекты класса Б.
- Наследование: реализует логические связи типа «является частным случаем».
- ///////////////////// объясняется в 1 билете.
Контейнер – это способ организации хранения данных. (стек, массив, связный список….)
Контейнерные классы -- это универсальные шаблонные классы, предназначенные для хранения элементов заданного типа в смежных областях памяти. Стандарт C++ уже включает в себя большое количество контейнеров, как часть STL (Standard Template Library -- Стандартная Библиотека Шаблонов).
Определяется контейнер, как:
контейнер<тип> объект;
Например: vector<int> example_vector; //создаем вектор целых чисел
Вложенные классы
Один класс может быть объявлен в другом классе, в этом случае внутренний класс называется вложенным:
class _class_1
{ class _class_2
{ · · ·
};
public:
· · ·
};
Доступ к компонентам вложенного класса, имеющим атрибут private, возможен только из функций вложенного класса и из функций внешнего класса, объявленных со спецификатором friend во вложенном классе.
class cls1 // внешний класс
{ class cls2 // вложенный класс
{ int b; // все компоненты private для cls1 и cls2
cls2(int bb): b(bb){} // конструктор класса cls2
};
public: // public секция для cls1
int a;
class cls3 // вложенный класс
{ int c; // private для cls1 и cls3
public: // public-секция для класса cls3
cls3(int cc): c(cc) {} // конструктор класса cls3
};
cls1(int aa): a(aa) {} // конструктор класса cls
};
void main()
{ cls1 aa(1980); // верно
cls1::cls2 bb(456); // ошибка cls2 cannot access private
cls1::cls3 cc(789); // верно
cout << aa.a << endl; // верно
cout << cc.c << endl; // ошибка 'c': cannot access private member
// declared in class 'cls1::cls3'
}
В приведенном тексте программы инструкция cls1::cls2 bb(456) является ошибочной, так как объявление класса cls2 и его компонента находятся в секции private. Для устранения ошибки при описании объекта bb необходимо изменить атрибуты доступа для класса cls2 следующим образом:
public:
Class cls2
{ int b; // private-компонента
public:
cls2(int bb): b(bb){}
};
Пример доступа к private-компонентам вложенного класса из функций внешнего класса, объявленных со спецификатором friend, приводится ниже.
class cls1 // внешний класс
{ int a;
public:
cls1(int aa): a(aa) {}
class cls2; // неполное объявление класса
void fun(cls1::cls2); // функция получает объект класса cls2
class cls2 // вложенный класс
{ int c;
public:
cls2(int cc): c(cc) {}
friend void cls1::fun(cls1::cls2); // ф-ция, дружественная классу cls1
int pp(){return c;}
};
};
void cls1::fun(cls1::cls2 dd) {cout << dd.c << endl << a;}
void main()
{ cls1 aa(123);
cls1:: cls2 cc(789);
aa.fun(cc);
};
Внешний класс cls1 содержит public-функцию fun(cls1::cls2 dd), где dd есть объект, соответствующий классу cls2, вложенному в класс cls1. В свою очередь в классе cls2 имеется friend-функция friend void cls1::fun(cls1::cls2 dd), обеспечивающая доступ функции fun класса cls1 к локальной компоненте с класса cls2.
11. Производные классы: простое наследование, основные понятия и определения. Правила определения производного класса, типы наследования, видимость членов класса. Реализация и использование конструкторов и деструкторов базового и производных классов. Использование экземпляров базового и производных классов. Указатели на базовый и производные классы.
Наследование – это свойство системы, позволяющее описать новый класс на основе уже существующего с частично или полностью заимствующейся функциональностью. Класс, от которого производится наследование, называется базовым или родительским. Новый класс – потомком, наследником или производным классом.
Например:
class A{ //базовый класс}; class B: public A{ //public наследование (public -> public, protected -> protected)}; class C: protected A{ //protected наследование (public, protected -> protected)}; class Z: private A{ //private наследование (public, protected -> private)};
Данные, спрятанные в private родительского класса, не доступны из производного класса.
class A {
private:
int data;
public:
A():data(0),vasya(0){}
A(int a, int b):data(a), vasya(b)
virtual ~A() {}
int vasya;
};
class B:public A {
private
int aa;
public:
B(int a, int b):aa(a), vasya(b)
~B(){}
func() {
int a;
a = data; //ошибка доступа (data была в private родительского класса)
a = vasya; // все ок (vasya был в public родительского класса)
};
};
int main() {
A* aaa = new A; // создание экземпляра класса А(пустой конструктор)
A* aaa_2 = new A(23,42) // создание экземпляра А с заданными параметрами
B* bbb_2 = new B(23, 52) // создание экземпляра B с заданными параметрами (лучше использовать данный вариант)
A* bbb = new B(23, 52) // создание экземпляра B с заданными параметрами (плохой вариант из-за высвобождения памяти в последствии)
};