Приведение структур (классов) в C++. Шпаргалка. Часть 2.

В первой части шпоры рассмотрел примеры работы с указателями и ссылками на простые типы. В этой части рассмотрю примеры приведения сложных типов.

Для простоты использую структуры вместо классов. Особо разницы никакой, но для примера проще.

От базовой структуры 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 объектами, а не указателями и в дальнейшем не выполнять приведение к отнаследованному типу, то при таком выделении памяти все будет работать корректно.

Spread the love
Запись опубликована в рубрике IT рецепты. Добавьте в закладки постоянную ссылку.

Добавить комментарий