Better solution than dynamic_cast in C++

时光怂恿深爱的人放手 提交于 2019-12-10 16:05:30

问题


I have a class hierarchy that I designed for a project of mine, but I am not sure how to go about implement part of it.

Here is the class hierarchy:

class Shape { };

class Colored { // Only pure virtual functions
};

class Square : public Shape { };

class Circle : public Shape { };

class ColoredSquare : public Square, public Colored { };

class ColoredCircle : public Circle, public Colored { };

In part of my project, I have a std::vector of different type shapes. In order to run an algorithm though, I need to put them in a std::vector of colored objects (all of which are derived types of different concrete shapes, so I need a method to cast a Square into a ColoredSquare and a Circle into a ColoredCircle at runtime. The tricky thing is that the 'shape' classes are in a different library than the 'colored' classes. What is the best method to acomplish this? I have thought about doing a dynamic_cast check, but if there is a better way, I would rather go with that.

Edit 1:

Here's a bit better of an Example:

class Traceable {
    public:
        // All virtual functions
        virtual bool intersect(const Ray& r) = 0;
        // ...
};

class TraceableSphere : public Sphere, public Traceable {
};

class IO {
    public:
        // Reads shapes from a file, constructs new concrete shapes, and returns them to
        // whatever class needs them.
        std::vector<Shape*> shape_reader(std::string file_name);
};

class RayTracer {
    public:
        void init(const std::vector<Shape*>& shapes);
        void run();
    private:
        std::vector<Traceable*> traceable_shapes;
};

void RayTracer::init(const std::vector<Shape*>& shapes) {
    // ??? traceable_shapes <- shapes
}

void RayTracer::run() {
    // Do algorithm
}

回答1:


You could use the decorator pattern:

class ColorDecorator public Colored
{
    ColorDecorator(Shape* shape): m_shape(shape) {}
    ... //forward/implement whatever you want
};

If you want to store a Square in a Colored vector, wrap it in such a decorator.

Whether this makes sense is questionable though, it depends on your design and the alternatives. Just in case, also check out the visitor pattern (aka double dispatch) which you could use to just visit a subset of objects in a container or treat them differently depending on their type.




回答2:


Looks like you are going to design the class library in a "is-a" style, welcome to the Inheritance-Hell. Can you elaborate a bit about your "algorithm" ? Typically it is bad design if you need to "type-test" on objects, since that is what you want to avoid with polymorphism. So the object should provide the proper implementation the algorithm uses (design-pattern: "strategy"), advanced concepts utilize "policy-based class design".




回答3:


With careful design, you can avoid casting. In particular, care for SRP. Implement methods carefully so that they use a single Interface to achieve a single goal/fulfill a single responsibility. You have not posted anything about the algorithms or how the objects will be used. Below is a hypothetical sample design:

class A {
  public:
        void doSomeThing();
};

class B{
  public:
        void doSomeOtherThing();
};

class C:public A,public B{};

void f1( A* a){
   //some operation
   a->doSomeThing();
   //more operation
}
void f2(B* b){
   //some operation
   b->doSomeOtherThing();
   //more operation

}

int main(int argc, char* argv[])
{
  C c;
  f1(&c);
  f2(&c);

  return 0;
}

Note using the object c in different context. The idea is to use only the interface of C that is relevant for a specific purpose. This example can have classes instead of the functions f or f2. For example, you have some Algorithms classes that do some operation using the objects in the inheritance hierarchy, you should create the classes such that they perform a single responsibility, which most of the time requires a single interface to use, and then you can create/pass objects as instance of that interface only.




回答4:


Object-oriented programming only makes sense if all implementations of an interface implement the same operations in a different way. Object-orientation is all about operations. You have not shown us any operations, so we cannot tell you if object-orientation even makes sense for your problem at all. You do not have to use object-oriented programming if it doesn't make sense, especially in C++, which offers a few other ways to manage code.

As for dynamic_cast -- in well-designed object-oriented code, it should be rare. If you really need to know the concrete type in some situation (and there are such situations in real-life software engineering, especially when you maintain legacy code), then it's the best tool for the job, and much cleaner than trying to reimplement the wheel by putting something like virtual Concrete* ToConcrete() in the base class.




回答5:


I think the simplest & cleanest solution for you would be something like the following similar to what Chris suggests at the end.

class Shape {
  virtual Colored *getColored() {
    return NULL;
  }
};

class Colored { // Only pure virtual functions
};

class Square : public Shape { };

class Circle : public Shape { };

class ColoredSquare : public Square, public Colored {
  virtual Colored *getColored() {
    return this;
  }
};

class ColoredCircle : public Circle, public Colored {
  virtual Colored *getColored() {
    return this;
  }
};

I do not completely understand this statement though " The tricky thing is that the 'shape' classes are in a different library than the 'colored' classes."

How does this not allow you to do what's being suggested here (but still allow you to create a class ColoredSquare) ?



来源:https://stackoverflow.com/questions/23569140/better-solution-than-dynamic-cast-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!