简体   繁体   English

比C ++中的dynamic_cast更好的解决方案

[英]Better solution than dynamic_cast in C++

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. 在我的项目的一部分,我有一个不同类型形状的std :: vector。 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. 为了运行算法,我需要将它们放在一个有色对象的std :: vector中(所有这些都是不同具体形状的派生类型,所以我需要一个方法将Square转换为ColoredSquare和Circle into一个运行时的ColoredCircle。棘手的是,'shape'类与'有色'类在不同的库中。实现这个的最佳方法是什么?我考虑过做一个dynamic_cast检查,但是如果有的话更好的方式,我宁愿一起去。

Edit 1: 编辑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
}

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. 如果要在彩色矢量中存储Square,请将其包装在这样的装饰器中。

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. 为了以防万一,还要检查访客模式(也称为双重调度),您可以使用它来访问容器中的对象子集,或根据其类型对它们进行不同的处理。

Looks like you are going to design the class library in a "is-a" style, welcome to the Inheritance-Hell. 看起来你要以“is-a”风格设计类库,欢迎来到继承地狱。 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". 因此,对象应该提供算法使用的适当实现(设计模式:“策略”),高级概念利用“基于策略的类设计”。

With careful design, you can avoid casting . 精心设计,可以避免casting In particular, care for SRP . 特别是,关心SRP Implement methods carefully so that they use a single Interface to achieve a single goal/fulfill a single responsibility. 仔细实施方法,以便他们使用单一Interface实现单一目标/履行单一责任。 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. 注意在不同的上下文中使用对象c The idea is to use only the interface of C that is relevant for a specific purpose. 我们的想法是使用与特定目的相关的C interface This example can have classes instead of the functions f or f2 . 此示例可以具有类而不是函数ff2 For example, you have some Algorithm s 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. 例如,您有一些Algorithm的类使用继承层次结构中的对象执行某些操作,您应该创建类,使它们执行单一职责,大多数时候需要单个界面才能使用,然后您可以创建/传递对象仅作为该接口的instance

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. 如果它没有意义,你不必使用面向对象的编程,特别是在C ++中,它提供了一些其他管理代码的方法。

As for dynamic_cast -- in well-designed object-oriented code, it should be rare. 至于dynamic_cast - 在精心设计的面向对象的代码中,它应该是罕见的。 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. 如果你真的需要在某些情况下知道具体类型(并且在现实生活中的软件工程中存在这种情况,特别是当你维护遗留代码时),那么它是最好的工具,比试图重新实现通过在基类中放置类似virtual Concrete* ToConcrete()的内容。

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) ? 这怎么不允许你做这里建议的(但仍然允许你创建一个ColoredSquare类)?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM