1

基于更有效的 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 . . .
4

4 回答 4

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.
于 2013-02-19T17:45:10.593 回答
2

没有什么神奇的std::vector。保存指向基类的指针的常规数组将同样工作。

于 2013-02-19T17:43:24.167 回答
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衰减为指向其第一个元素的指针。那是 a derived*,编译器将该指针转换为 abase*并将其传递给f. 在 内部f,指针算术将传入的指针视为base*. p[1]指向地址为sizeof(base)bytes above的对象p,即指向derived数组中第一个对象的中间。

因此,除了第一个之外,您对数组元素所做的任何事情都会让您胡说八道。

于 2013-02-19T18:07:04.110 回答
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());
于 2013-02-19T17:46:19.610 回答