基于更有效的C ++ 第3项:永远不要多态地处理数组,我们应该避免多态地处理数组。
那么为什么我们可以使用std::vector
来保存指向基类的指针而没有问题?
谢谢
#include <iostream>
#include "boost/shared_ptr.hpp"
#include <vector>
class BaseClass {
public:
virtual void PrintMe() const {
std::cout << "BaseClass" << std::endl;
}
virtual ~BaseClass() {}
};
class SubClass : public BaseClass {
public:
virtual void PrintMe() const {
std::cout << "SubClass" << std::endl;
}
};
int main()
{
std::vector<boost::shared_ptr<BaseClass> > vecPtrs;
boost::shared_ptr<BaseClass> shp1(new BaseClass);
vecPtrs.push_back(shp1);
boost::shared_ptr<BaseClass> shp2(new SubClass);
vecPtrs.push_back(shp2);
for (size_t i = 0; i < vecPtrs.size(); ++i)
{
vecPtrs[i]->PrintMe();
}
}
// Output:
BaseClass
SubClass
Press any key to continue . . .
答案 0 :(得分:10)
可以使用数组或向量将指针保存到多态类型。
问题是,如果您尝试以多态方式处理对象数组。通过指针访问数组使用指针类型来确定数组对象的大小,如果指针类型与对象类型不匹配,则会出现严重错误。
Base * stuff = new Derived[10]; // No compile error: pointer conversion is allowed
stuff[2].do_something(); // Still no compile error, but weird runtime errors.
答案 1 :(得分:2)
std::vector
没有任何魔力。保存指向基类的指针的常规数组也可以正常工作。
答案 2 :(得分:2)
问题在于:
struct base {
int element;
};
struct derived : base {
int another_element;
};
void f(base *p) {
std::cout << (void*)&p[1] << '\n';
}
int main() {
derived array[20];
std::cout << (void*)&array[1] << '\n';
f(array);
return 0;
}
如果您运行此程序,您将获得阵列中索引1处元素的两个不同地址。
这是因为在调用f
时,名称array
衰减为指向其第一个元素的指针。这是derived*
,编译器将指针转换为base*
并将其传递给f
。在f
内,指针算术将传入的指针视为base*
。 p[1]
指向一个地址为sizeof(base)
字节高于p
的对象,也就是说,它指向数组中第一个derived
对象的中间位置。
因此,除了第一个数组元素之外,您使用数组元素所做的任何操作都会让您无意义。
答案 3 :(得分:1)
它说你不应该这样做:
Base array[N];
array[0] = Derived1();
array[1] = Derived2();
这些派生对象在放入数组时会被切片。
对于像std::vector
这样的标准容器,情况也是如此。对于C ++中的多态行为,您需要使用指针:
std::unique_ptr<Base> array[N];
array[0].reset(new Derived1());
array[1].reset(new Derived2());