C++: делаем вектор из разнотипных объектов класса (родителя и потомков)
В этой лекции по классам аналогичная задача решалась просто организацией статического массива указателей, элементы которого могли хранить адреса как экземпляров класса-предка, так и потомков.
Можно ли сделать то же самое с помощью стандартного контейнера vector?
Конечно, можно, покажем это на простом примере. Программе понадобится стандартная заголовочная секция
#include <iostream> #include <vector> using namespace std;
после которой опишем класс Parent
, содержащий общие для всех классов-потомков возможности:
class Parent { public: void print() { cout << "\nPrinting!"; } //Метод для вывода чего-то protected: virtual ~Parent(){}; //Деструктор нужно сделать виртуальным, так как класс базовый };
Предусмотрим 2 класса-потомка, в одном из которых будет публичный метод print1
, а в другом - приватный метод print2
:
class Type1 : public Parent { public: void print1() { cout << "\n1"; } }; class Type2 : public Parent { void print2() { cout << "\n2"; } };
В функции main
создадим пустой вектор из объектов базового класса (так нужно делать всегда!), потом добавим в него
разнотипные объекты:
int main() { vector <Parent *> vec; //Вектор создаётся vec.push_back(new Type1()); //Добавляем разнотипные объекты vec.push_back(new Type2()); vec.push_back(new Type1());
Через вектор vec
можно без каких-либо проблем вызывать публичные методы родителя:
vec[0]->print();
А вот вызвать "напрямую" метод потомка не получится:
//vec[0]->print1(); //Так нельзя
Чтобы это сделать, нужно применить указатель и преобразование типа:
Type1 *t1 = (Type1 *)vec[0]; t1->print1(); //А так можно, если print1 имеет доступ public
Будет возможно вызывать и приватные методы потомка, но только если:
- в описание класса
Parent
добавить методpublic: virtual void print2(){};
с пустым телом виртуальной функции
print2
. Если сделать, например,virtual void print2()=0;
класс станет абстрактным и потребует реализации метода
print2
во всех потомках. - перегрузить метод
print2
в нужных потомках (у нас это уже сделано в классеType2
).
Теперь код вида
vec[1]->print2();
работает. Осталось закрыть программу, вот что получилось окончательно:
#include <iostream> #include <vector> using namespace std; class Parent { public: void print() { cout << "\nPrinting!"; } //Метод для вывода чего-то virtual void print2() {}; //Пустой витуальный метод для перегрузки в потомках protected: virtual ~Parent() {}; //Деструктор нужно сделать виртуальным, так как класс базовый }; class Type1 : public Parent { public: void print1() { cout << "\n1"; } }; class Type2 : public Parent { void print2() { cout << "\n2"; } }; int main() { vector <Parent *> vec; //Вектор создаётся vec.push_back(new Type1()); //Добавляем разнотипные объекты vec.push_back(new Type2()); vec.push_back(new Type1()); vec[0]->print(); //В Type1 метод print1 публичен //vec[0]->print1(); //Просто так вызвать через список метод потомка нельзя Type1 *t1 = (Type1 *)vec[0]; t1->print1(); //А так можно, если print1 имеет доступ public vec[1]->print2(); //В Type2 метод print2 приватен, но он перегруженный cin.get(); return 0; }
Пример проверен в Visual Studio 2015. Могли мы попробовать и dynamic_cast
, но успеха бы вот такой, например, код не принёс:
Parent *p = vec[0]; Type2 *t2 = dynamic_cast <Type2 *> (p); //Преобразовал тип данных родителя к потомку //t2->print2(); //но приватный метод потомка всё равно не вызвать!
14.03.2018, 10:02 [9159 просмотров]