Reputation: 451
I am creating an abstract geometry class that has children classes. However, I want that the class RightCircularCone
also has its own private variables that define its apex coordinates, such that the abstract class is not unnecessary big in memory size for objects of type Sphere
that don't need storage of apex variables.
However, I can't seem to access the functions and variables of RightCircularCone
when I load them from a container that uses smart pointers, as it keeps being defined as its parent class Shape
. Can anyone see what is going wrong?! Appreciate it!
/* 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);
};
The classes defined above are used in another .cpp file where methods of a class are defined:
#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
}
Now, when I use an iterator over the map std::map<unsigned int, std::shared_ptr<Shape> > shapeList;
as defined in part.cpp
I can never access the methods of children classes Sphere
and RightCircularCone
as the map returns a type of Shape
, even though I used a smart pointer!!!
Anybody knows why and a potential fix (or neater way to set up the classes)??
Thanks!
//EDIT:
This is the error I get:
error: no member named 'PrintApex' in 'Shape'
iterator->second->PrintApex();
Upvotes: 1
Views: 1171
Reputation: 2453
Think about it. You create a vector of shared_ptr of Shape. As far as the vector is concerned you are storing Shape instances in it, not Sphere or whatever.
Now, you happen to initialize your Shape instance with a Sphere and you store the ptr of that Sphere into the vector. Next item is a RightCircularCone, again stored as a Shape.
You access the first element, and as far as the compiler is concerned at THAT point, you only have a Shape. It cannot deduce the actual type as this happens at runtime. So, as far as the compiler is concerned you have a Shape instance with whatever the Shape class contains.
Now, you need to somehow inform the compiler about the type you want to work with, so it can find the methods you want to access. For that, you use dynamic_cast to specify the intent that this is a Sphere and you should have access to Sphere members.
More details about dynamic_cast, here http://en.cppreference.com/w/cpp/language/dynamic_cast
Edit. A note about design.
In principle, you want to offload as much to the compiler. If there is something that compiler can do, let him do it. So, if you want to do something different for each subclass of Shape, instead of checking a string literal for the Shape type you could use virtual specialized functions that act on a specific type. E.g.
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
it may seem a bit more work, but in the long run it is actually simpler as you offload the picking of the functionality to someone else.
Upvotes: 1