从 vector<Parent* 中获取派生类对象>



我知道可以在向量中存储一堆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*失败(当然),因为ImageBase中未定义GetPixel。我知道你可以dynamic_cast<>到一堆类型,以确定子类是否与其中任何一个匹配,但这个列表可能很快就会变得很长。如果长列表可以驻留在一个地方,那么它就可以了,但是你将如何创建一个函数来实现这一点呢?该函数需要一个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都是库的一部分,所以我无法更改它们。

访问者模式来恢复类型信息,可能有一个模板化的助手来实现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)。

最新更新