В первой части шпоры рассмотрел примеры работы с указателями и ссылками на простые типы. В этой части рассмотрю примеры приведения сложных типов.
Для простоты использую структуры вместо классов. Особо разницы никакой, но для примера проще.
От базовой структуры Base наследуется структура Nested.
struct Base
{
int a;
int b;
std::string name = "Base";
void print()
{
printf("BaseConnection\r\n\tname=%s", name.c_str());
}
};
struct Nested : Base
{
int c;
std::string name = "Nested";
void print()
{
printf("NestedConnection\r\n\tname=%s", name.c_str());
}
};Можно поместить объект структуры Nested в Base, но чтобы затем обратится к свойствам Nested нужно выполнить операцию приведения типов.
printf("\r\n--------------Наследование и приведение структур-----------------\r\n");
Nested nested;
nested.a = 5;
nested.b = 10;
nested.c = 15;
Base* base = &nested; //Прямое преобразование(расширение) - неявное
base->print();
Nested* nested1 = static_cast<Nested*>(base);
nested1->print();
printf("\r\n--------------Распечатка значений base объекта после приведения к типу Nested-----------------\r\n");
printf("Nested\r\n\ta=%i,\r\n\tb=%i,\r\n\tc=%i\r\n\tname=%s\r\n", nested1->a, nested1->b, nested1->c, nested1->name.c_str());
printf("\r\n--------------Обратное преобразование (приведение)-----------------\r\n");
//Base* pbase = (Base*)nested1; // Обратное преобразование (сужение) - явное
Base* pbase = static_cast<Base*>(nested1);
pbase->print();
printf("\r\n--------------Распечатка значений nested1 объекта после приведения к базовому типу Base-----------------\r\n");
printf("Base\r\n\ta=%i,\r\n\tb=%i,\r\n\tname=%s\r\n", pbase->a, pbase->b, pbase->name.c_str());В результате выполнения этого кода получим следущее:
--------------Наследование и приведение структур-----------------
BaseConnection
name=Base
NestedConnection
name=Nested
---Распечатка значений base объекта после приведения к типу Nested-----
Nested
a=5,
b=10,
c=15
name=Nested
--------------Обратное преобразование (приведение)-----------------
BaseConnection
name=Base
---Распечатка значений nested1 после приведения к базовому типу Base---
Base
a=5,
b=10,
name=BaseНаследование и приведение структур и помещение в динамический массив vector
После того как от типа Base отнаследовали структуру Nested поместим в vector для хранения указателей на объекты с типом Base.
При помещении вектор объекта с отнаследованным типом Nested память под объект этого типа должна быть зарезервирована и освобождать её нельзя, иначе при обратном приведении указатель будет ссылася на произвольный участок памяти, где объект типа Nested уже не содержится.
Поэтому в примере резервируется память под массив содержащий два объекта с типом Nested.
printf("\r\n--------------Наследование и приведение структур в vector-----------------\r\n");
std::vector<Base*> bases;
//--------------Резервируем память на два объекта с типом Nested----------------
Nested* nested0 = new Nested[2];
//--------------Заполняем первый объект типа Nested-----------------
nested0[0].a = nested.a;
nested0[0].b = nested.b;
nested0[0].c = nested.c;
//--------------Помещаем первый объект типа Nested в vector принимающий указатель на тип Base-----------------
bases.push_back(&nested0[0]);
//--------------Заполняем второй объект типа Nested-----------------
nested0[1].a = nested.a + 10;
nested0[1].b = nested.b + 10;
nested0[1].c = nested.c + 10;
//--------------Помещаем второй объект типа Nested в вектор принимающий указатель на тип Base-----------------
bases.push_back(&nested0[1]);
//--------------Распечатываем вектор с ссылками на объект Base-----------------\r\n");
for (Base* base : bases)
{
printf("\r\n--------------Приводим указатель на объект типа Base к объекту типа Nested-----------------\r\n");
Nested* nested2 = static_cast<Nested*>(base);
nested2->print();
printf("\r\n--------------Распечатываем значение Base объекта приведенного к типу Nested-----------------\r\n");
printf("Nested\r\n\ta=%i,\r\n\tb=%i,\r\n\tc=%i\r\n\tname=%s\r\n", nested2->a, nested2->b, nested2->c, nested2->name.c_str());
}Результат работы программы
--------------Наследование и приведение структур в vector-----------------
--------------Приводим указатель на объект типа Base к объекту типа Nested-----------------
NestedConnection
name=Nested
--------------Распечатываем значение Base объекта приведенного к типу Nested-----------------
Nested
a=5,
b=10,
c=15
name=Nested
--------------Приводим указатель на объект типа Base к объекту типа Nested-----------------
NestedConnection
name=Nested
--------------Распечатываем значение Base объекта приведенного к типу Nested-----------------
Nested
a=15,
b=20,
c=25
name=NestedЕщё раз отмечу, что в vector нужно помещать указатели на объекты Nested для которых память зарезервирована. Если в vector поместить не указатель на объект Base, а непосредственно объект Nested, то в vector останутся только значения класса Nested обрезанные до типа Base. При этом обратное восстановление будет невозможно, данные уже затерты.
//--------------Инициализируем vector типом Base-----------------
std::vector<Base> bases1;
//--------------Помещаем первый объект типа Nested в вектор принимающий объект с типом Base-----------------
bases1.push_back(nested0[0]);
//--------------Помещаем второй объект типа Nested в вектор принимающий объект с типом Base-----------------
bases1.push_back(nested0[1]);
//--------------Распечатываем вектор с объектами типа Base---------------- for (Base base : bases1)
{
printf("\r\n--------------Приводим объект типа Base к объекту типа Nested-----------------\r\n");
Nested* nested3 = static_cast<Nested*>(&base);
nested3->print();
printf("\r\n--------------Распечатываем значение Base объекта приведенного к типу Nested-----------------\r\n");
printf("Nested\r\n\ta=%i,\r\n\tb=%i,\r\n\tc=%i\r\n\tname=%s\r\n", nested3->a, nested3->b, nested3->c, nested3->name.c_str());
printf("\r\n--------------Видим, что после приведения значение поля c заполнено случайными значениями-----------------\r\n");
}Результаты заполнения vector объектами вместо указателя на объекты.
--------------Приводим объект типа Base к объекту типа Nested-----------------
NestedConnection
name=ээээ
--------------Распечатываем значение Base объекта приведенного к типу Nested-----------------
Nested
a=5,
b=10,
c=-858993460
name=ээээ
--------------Видим, что после приведения значение поля c заполнено случайными значениями-----------------
--------------Приводим объект типа Base к объекту типа Nested-----------------
NestedConnection
name=ээээ
--------------Распечатываем значение Base объекта приведенного к типу Nested-----------------
Nested
a=15,
b=20,
c=-858993460
name=ээээ
--------------Видим, что после приведения значение поля c заполнено случайными значениями-----------------Если заполнять vector объектами, а не указателями и в дальнейшем не выполнять приведение к отнаследованному типу, то при таком выделении памяти все будет работать корректно.