繁体   English   中英

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

[英]Getting derived class objects out of a 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;
}

输出(图像); 当然,其中“图像”是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 )。

暂无
暂无

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

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