2015-01-21 3 views
0

Я создаю абстрактный класс геометрии, который имеет дочерние классы. Тем не менее, я хочу, чтобы класс RightCircularCone также имел свои собственные переменные, которые определяют его вершинные координаты, так что абстрактный класс не является ненужным большим по размеру памяти для объектов типа Sphere, которым не требуется хранить переменные вершины. Однако я не могу получить доступ к функциям и переменным RightCircularCone, когда я загружаю их из контейнера, который использует интеллектуальные указатели, поскольку он продолжает определяться как его родительский класс Shape. Кто-нибудь может понять, что происходит? Ценить это!Абстрактные классы наследования и контейнеры интеллектуальных указателей

/* shapes.hpp */ 
class Shape{ 
    public: 
     unsigned int color; 
     float radius; 
     float x,y,z; 
    public: 
     void SetSpatial(float radius, float x, float y, float z); 
     unsigned int GetColor(void); 
     void SetColor(unsigned int color); 
     virtual bool PointInside(const std::array<double,3> &point)=0; 
}; 

class RightCircularCone : public Shape{ 
    private: 
     float Ax,Ay,Az; 
    public: 
     bool PointInside(const std::array<double,3> &point); 
     void SetApex(float x, float y, float z); 
     void PrintApex(); 
}; 

class Sphere : public Shape{ 
    public: 
     bool PointInside(const std::array<double,3> &point); 
}; 

Классы, определенные выше, используются в другом файле .cpp, на котором определены методы класса:

#include "../../include/part.hpp" /* includes shapes.hpp in turn */ 

void Part::ReadPartFile(std::string partfile){ 
    try{ 
     std::ifstream dataFile; 
     dataFile.open(partfile); 
     //do checks, error badbits etc... 
     std::string word; 
     unsigned int counter=0; 
     while(!dataFile.eof()){ 
      dataFile >> word; 
      if(word == "sphere"){ 
       auto newSphere = std::make_shared<Sphere>(); 
       // load variables into objects from file by checking each word by using setColor and setSpatial 
       shapeList[counter++] = newSphere; 
      } else if(word == "rccone"){ 
       auto newRccone = std::make_shared<RightCircularCone>(); 
       // load variables into objects from file by checking each word by using setColor and setSpatial and setApex 
       shapeList[counter++] = newRccone; 
      } 
     } 
     dataFile.close(); 
    } catch(std::ifstream::failure e) { 
     //do exception handling here if necessary 
} 

Теперь, когда я использую итератор по карте std::map<unsigned int, std::shared_ptr<Shape> > shapeList;, как определено в part.cpp смогу никогда не обращайтесь к методам дочерних классов Sphere и RightCircularCone, так как карта возвращает тип Shape, хотя я использовал умную указатель !!! Кто-нибудь знает, почему и потенциальное решение (или более простой способ настроить классы)? Спасибо!

// EDIT:

Это ошибка, я получаю:

error: no member named 'PrintApex' in 'Shape' 
           iterator->second->PrintApex(); 
+2

Снова распространенная ошибка, отсутствие проверки извлечения потока: 'dataFile >> word;' –

+1

Умный указатель не волшебная палочка. Почему вы ожидаете, что это сделает волшебный материал для вас? –

+0

Ну, умные указатели в контейнере делают именно это. По крайней мере, они должны! Обычно, когда вы создаете контейнер типа родительского класса, вы можете хранить в нем тип дочернего класса, чтобы при извлечении его из контейнера вы могли использовать методы/vars этого дочернего класса, правильно? –

ответ

1

Подумайте об этом. Вы создаете вектор shared_ptr формы. Что касается вектора, вы храните в нем экземпляры Shape, а не Sphere или что-то еще.

Теперь вы инициализируете экземпляр Shape сферой, и вы храните ptr этой сферы в вектор. Следующий элемент - это RightCircularCone, снова сохраненный как форма.

Вы получаете доступ к первому элементу и, насколько компилятор относится к точке THAT, у вас есть только форма. Он не может вывести фактический тип, как это происходит во время выполнения. Итак, что касается компилятора, у вас есть экземпляр Shape с любым классом Shape.

Теперь вам нужно как-то сообщить компилятору о типе, с которым вы хотите работать, чтобы он мог найти методы, к которым вы хотите получить доступ. Для этого вы используете dynamic_cast, чтобы указать намерение, что это Sphere, и вы должны иметь доступ к членам Sphere.

Более подробно о dynamic_cast, здесь http://en.cppreference.com/w/cpp/language/dynamic_cast

Edit. Замечание о дизайне.

В принципе, вы хотите разгрузить как можно больше компилятору. Если есть что-то, что может сделать компилятор, пусть он это сделает. Итак, если вы хотите сделать что-то другое для каждого подкласса Shape, вместо проверки строкового литерала для типа Shape вы можете использовать виртуальные специализированные функции, которые действуют на определенный тип. Например.

virtual void printMe(Sphere & sphere) { 
cout << "This is a sphere with radious x" << endl; 
} 

virtual void printMe(RightCircularCone & cone) { 
cout << "This is a cone" << endl; 
} 

//and you use like 

for (auto & shape: shapes) { printMe(shape); } 

//and the correct functions are resolved automagically 

это может показаться немного больше работы, но в конечном счете, это на самом деле проще, так как вы разгружать комплектование функциональности для кого-то другого.

+0

Пожалуйста, не предлагайте 'dynamic_cast' программистам, которые не понимают наследование. –

+0

@ н.м. Вот почему ссылка на cppref. Он должен прочитать этот материал –

+0

«Теперь вам нужно как-то сообщить компилятору о типе, с которым вы хотите работать, чтобы он мог найти методы, к которым вы хотите получить доступ. Для этого вы используете dynamic_cast, чтобы указать, что это Сфера, и у вас должен быть доступ к членам Sphere ». Пожалуйста, не предлагайте это решение неопытным людям. Это просто плохая техника. cppreference не скажет вам, что это ссылка на C++. Нужен ресурс, посвященный объектно-ориентированному программированию и дизайну, а не конкретному языку. –

Смежные вопросы