В первой части шпоры рассмотрел примеры работы с указателями и ссылками на простые типы. В этой части рассмотрю примеры приведения сложных типов.
Для простоты использую структуры вместо классов. Особо разницы никакой, но для примера проще.
От базовой структуры 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 объектами, а не указателями и в дальнейшем не выполнять приведение к отнаследованному типу, то при таком выделении памяти все будет работать корректно.