Interested in a question on derived classes. There is a product class and it has 2 subclasses of food , and clothing . Each derived class has an individual method (SetCreateDate in food for example). For storage, I use the base class vector, but when I needed to know to which derived class the line inside the vector belongs and call the unique method of the derived class from it, I was faced with the problem of being unable to call. As I understand it, I either incorrectly map to the vector, or do I need to store data outside the vector?

Below is the entire code and an indication of the place of difficulty, here is a link to the archive with files (project for CodeBlocks)

food.h :

#ifndef FOOD_H #define FOOD_H #include <iostream> #include <string.h> #include "product.h" using namespace std; class food: public product { public: food(string, int, string); void show(); void SetCreateDate(string); protected: private: string CreateDate; }; #endif // FOOD_H 

food.cpp

  #include "food.h" food::food(string N, int P, string M) { Name=N; Volume=P; CreateDate=M; } void food::SetCreateDate(string cat) //индивидуальный метод этого подкласса { CreateDate = cat; } void food::show() { cout << "Имя товара: " << Name << " Цена:" << Volume << "Дата производства: " << CreateDate <<endl; } 

clothing.h :

 #ifndef CLOTHING_H #define CLOTHING_H #include <iostream> #include <string.h> #include "product.h" using namespace std; class clothing: public product { public: clothing(string, int, string); void show(); void SetCSize(string); protected: private: string CSize; }; 

clothing.cpp :

 #include "clothing.h" clothing::clothing(string N, int P, string Pr) { Name = N; Volume = P; CSize = Pr; } void clothing::SetCSize(string cat) //индивидуальный метод этого подкласса { CSize = cat; } void clothing::show() { cout << "Имя товара: " << Name << " Цена:" << Volume << "Размер: " << CSize <<endl; } 

product.h :

 #ifndef PRODUCT_H #define PRODUCT_H #include <iostream> #include <string.h> using namespace std; class product { public: product(); ~product(); virtual void show(); void SetName(string); int GetVolume(); void SetVolume(int); string ReturnType(); protected: int Volume; string Name; private: }; #endif // PRODUCT_H 

product.cpp :

 #include "product.h" product::product(): Volume(0) { Name="Noname"; } int product::GetVolume() { return Volume; } void product::SetName(string N) { Name=N; } void product::SetVolume(int P) { Volume=P; } void product::show() { cout<<"Name: "<<Name<<" Volume: "<<Volume<<endl; } product::~product() { //dtor } 

main.cpp

 #include <vector> #include <stdlib.h> #include "product.h" #include "food.h" #include "clothing.h" using namespace std; int main() { setlocale(LC_ALL, "Russian"); vector<product> data_arr; food row1("Сыр Мацарелла", 229, "Продкуты питания"); data_arr.push_back(row1); food row2("Кофе Якобс", 183, "Продкуты питания"); data_arr.push_back(row2); clothing row3("Джинсы регелар муж.", 1990, "Одежда"); data_arr.push_back(row3); clothing row4("Толстовка Ults.", 1970, "Одежда" ); data_arr.push_back(row4); for (int i = 0; i < data_arr.size(); i++) { data_arr[i].show(); } char Key; cout << "\n" << "Желаете что-нибудь изменить?(y/n)" <<endl; cin >> Key; if (Key=='y') { recursive_command_row: system("cls"); for (int i = 0; i < data_arr.size(); i++) { data_arr[i].show(); } int command_num; string command_row; cout << "\n" << "Введите номер строки, в которой хотите сделать изменения" << endl; cin >> command_num; if(command_num <= data_arr.size()) { //вот тут нужно узнать к какому подклассу принадлежит строка и data_arr[command_num] и вызвать уникальный метод } else { goto recursive_command_row; } } } 

2 answers 2

Better to do a virtual function in the base class

 public: virtual void setCreateDate(string) = 0; 

and in the vector store not objects, but pointers to them. Example:

 #include <iostream> #include <vector> using namespace std; class B { public: B() {} virtual ~B() {} virtual void f() = 0; }; class D1 : public B { public: D1() {} ~D1() {} void f() { cout << "I'm a D1" << endl; } }; class D2 : public B { public: D2() {} ~D2() {} void f() { cout << "I'm a D2" << endl; } }; int main() { vector<B*> v; v.push_back(new D1); v.push_back(new D2); v[0]->f(); v[1]->f(); return 0; } 

The output of the program:

 I'm a D1 I'm a D2 
  • In 2016, using bare pointers to objects is an example of a very bad code. This is highly discouraged. It is much more logical to use smart pointers, of course. - user1056837

The problem is that your vector stores objects of the base type, respectively, the base type does not know about the methods that the heirs have. Either you need to add an abstract method to the base type and implement it with the heirs, or check the type of something like

 food* f = dynamic_cast<food*>(&(data_arr[i])); if (f != NULL) f->SetCreateDate("1/1/2001"); 

but to put it mildly not quite a beautiful decision

  • one
    What do you push_back , when you push_back a derived class object into a vector of base class objects, it is simply truncated and no dynamic_cast will help. Store need pointers. - int3
  • @Harry: dynamic_cast , of course, does not solve any problem here and can not solve it in principle. If objects like product are stored in a vector, then no dynamic_cast can ever turn them into food . Such a dynamic_cast will always be guaranteed to return a null pointer. - AnT
  • It is also worth noting that no “adding an abstract method” will help here either. Adding an abstract (pure?) Method to a product will only cause the vector<product> stop compiling altogether. In any case, polymorphism does not help here: a vector<product> can only contain objects of type product and no other. No polymorphism in such a vector is possible. As already noted, polymorphism in such containers is possible only if the container stores pointers / references to objects, and not the objects themselves. - AnT