众所周知,对数组的项目进行多态处理是危险的,因为一般而言,基类和派生类的大小不同,并且指针算法很可能会失败。
void doSomethingOnBases(Base* bases, int numBases)
{
for(int i = 0; i < numBases; i++)
{
bases[i].doSomething();
}
}
//...
//...
Derived deriveds[60]; // declare 60 derived objects
doSomethingOnBases(deriveds, 60); // will crash and burn
//...
//...
但是,如果我这样做了怎么办?
template <class X>
void doSomethingOnArray(X* arr, int numItems)
{
for(int i = 0; i < numItems; i++)
{
arr[i].doSomething();
}
}
//...
//...
Derived deriveds[60]; // declare 60 derived objects
doSomethingOnArray(deriveds, 60);
//...
//...
在这种情况下,编译器将看到Derived*
被传入并生成doSomethingOnArray(Derived*, int)
,这是一个精确的类型匹配,因此,没有指针算术错误。
如果我可以保证doSomethingOnArray()
中取消引用的任何对象都具有doSomething()
方法,这是一种伪造多态性的安全方法吗?
答案 0 :(得分:2)
可以使用模板,但是另一种解决方案是传递Base** bases
,在其中保存指向派生自Base的对象的指针的数组。
void doSomethingOnBases(Base** bases, int numBases)
{
for(int i = 0; i < numBases; i++)
{
bases[i]->doSomething();
}
}
这将允许您保留混合的派生对象的数组,而不仅仅是单个类型Derived
的数组。
顺便说一句:请注意,如果要使用模板化解决方案,那么您甚至都不需要从Base继承。
答案 1 :(得分:1)
只要您像在第二个示例中那样,而不是像
doSomethingOnBases<Base>(deriveds, 60);
您应该没事。如果您想让它特定于数组,请考虑这样编写模板:
template <typename X, std::size_t N>
void doSomethingOnArray(X (&arr)[N])
{
for(auto a = &arr[0]; a != &arr[0] + N; ++a)
a->doSomething();
}
此版本的优点在于它不会真正被错误地调用。它只能在实际数组上使用,它会根据参数自动推断数组的大小和元素类型。
另外,请注意,您可以简单地使用基于范围的for循环
for (auto& a : arr)
a->doSomething();
或
for (Base& b : arr)
…
如果由于某种原因确实需要基类引用...