从派生<parent *> </parent *>中获取派生类对象

时间:2012-01-09 20:39:26

标签: c++

我知道你可以在向量中存储一堆Parent *对象。但是,如果无法在父类中定义需要调用对象的函数(它们依赖于子类的模板参数),那么您应该如何从容器中检索对象?

在这个例子中:

#include <iostream>

class ImageBase{};

template <typename TPixel>
class Image : public ImageBase
{
public:
  TPixel GetPixel() const {TPixel a; return a;}
};

template<typename TImage>
void Output(const TImage* image)
{
  std::cout << image->GetPixel();
}

int main(int, char *[])
{
  // Works correctly
  {
  Image<float>* image = new Image<float>;
  image->GetPixel();
  Output(image);
  }

  {
  ImageBase* image = new Image<float>;
  Output(image);
  }

  return 0;
}

输出(图像);其中'image'是ImageBase *失败(当然)因为GetPixel未在ImageBase中定义。我知道你可以dynamic_cast&lt;&gt;到一堆类型来判断子类是否与它们中的任何一个匹配,但是这个列表很快就会变得非常长。如果它可以驻留在一个地方,那么长列表就可以了,但你怎么能做到这个呢?该函数将采用ImageBase *,但它会返回什么?

returnType? GetSubclass(ImageBase* input)
{
  if(dynamic_cast<Image<float>*>(input))
    {
    return Image<float>*;
    }
  else if(dynamic_cast<Image<int>*>(input))
    {
    return Image<int>*;
    }
}

对我来说,希望能够在子类上调用一些模板函数似乎是合理的,这些子类只能通过模板参数(如本例中的设置)而在签名上有所不同,不是吗?

在我的实际案例中,Image和ImageBase都是库的一部分,因此我无法更改它们。

1 个答案:

答案 0 :(得分:3)

访问者模式以恢复类型信息,可能使用实现visit函数的模板化助手。

首先,让我们将您的算法变成多态仿函数对象:

struct Output
{
    std::ostream& dest;
    Output(std::ostream& destination) : dest(destination) {}

    template<typename PixelType>
    void operator()(const Image<PixelType>* image) const
    {
        dest << image->GetPixel();
    }
};

现在,让我们添加一个访问者界面:

struct ImageVisitor /* abstract */
{
    virtual void Visit(Image<RGBQUAD>*) const = 0;
    virtual void Visit(Image<RGBTRIPLE>*) const = 0;
    virtual void Visit(Image<RGBQUAD16>*) const = 0;
    virtual void Visit(Image<RGBTRIPLE16>*) const = 0;
    virtual void Visit(Image<RGBQUADF>*) const = 0;
    virtual void Visit(Image<RGBTRIPLEF>*) const = 0;
    virtual void Visit(Image<RGBQUADD>*) const = 0;
    virtual void Visit(Image<RGBTRIPLED>*) const = 0;
};

和货运代理人:

template<typename Functor>
struct ImageVisitorShim : ImageVisitor
{
    Functor& fn;
    ImageVisitorShim(Functor& algorithm) : fn(algorithm) {}

    virtual void Visit(Image<RGBQUAD>     *im) const { fn(im); }
    virtual void Visit(Image<RGBTRIPLE>   *im) const { fn(im); }
    virtual void Visit(Image<RGBQUAD16>   *im) const { fn(im); }
    virtual void Visit(Image<RGBTRIPLE16> *im) const { fn(im); }
    virtual void Visit(Image<RGBQUADF>    *im) const { fn(im); }
    virtual void Visit(Image<RGBTRIPLEF>  *im) const { fn(im); }
    virtual void Visit(Image<RGBQUADD>    *im) const { fn(im); }
    virtual void Visit(Image<RGBTRIPLED>  *im) const { fn(im); }
};

还有一家工厂:

template<typename Functor>
ImageVisitorShim<Functor> MakeImageVisitor(Functor& f) { return f; }

现在是符合访问者标准的图片包装器:

struct VisitableImageBase
{
    virtual void VisitWith(const ImageVisitor&) = 0;
};

template<typename PixelType>
struct VisitableImage : VisitableImageBase
{
    unique_ptr<Image<PixelType>> content; // or shared or raw pointer, if ownership is elsewhere

    VisitableImage(Image<PixelType>* im) : content(im) {}

    virtual void VisitWith(const ImageVisitor& v) { v.Visit(content.get()); }
};

最后,您可以使用多态图像矢量!

vector<unique_ptr<VisitableImageBase>> images;
Output outputter(std::cout);
for( auto vim : images ) vim->VisitWith(MakeImageVisitor(outputter));

这是很多代码,但好处是只要使用模板实现仿函数,就可以添加新类型而不会影响现有的仿函数(只需扩展垫片)。并且不需要太多代码来添加更多图像处理函数(只是一个新的模板仿函数类,类似于Output)。