БлогNot. C++: делаем вектор из разнотипных объектов класса (родителя и потомков)

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 [8564 просмотра]


теги: c++ программирование список

К этой статье пока нет комментариев, Ваш будет первым