![](/img/trans.png)
[英]Getting an object of a specific derived class, from a vector of base class objects
[英]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.