混合数组和多态的编译器警告

时间:2016-09-02 16:05:38

标签: c++ arrays polymorphism

在更有效的C ++中,提出一个有趣的观点是混合数组和多态是一个坏主意。例如:

class Base {
    public:
    Base(int y) : a(y) {}
    int a;
};

class D : public Base {
    public:
    D(int w, int y) : Base(y), c(w) {}
    int c;
};

std::ostream& operator<<(std::ostream& os, const Base &obj )
{
    os << obj.a << std::endl;
    return os;
}

// This function will work perfectly well if i pass in a `Base` array, 
// but if i pass in `D` array we are going to run into some problems. 
// Namely that `arr[i+1] = &arr[i] + sizeof(Base)` will not progress 
// the array correctly for a `D` array.
void printArray(const Base arr[]) {
    for (int i = 0; i < 5; ++i) {
        std::cout << arr[i];
    }
}

int main() {
   D arr[5] = { D(0, 10), D(1, 11), D(2, 12), D(3, 13), D(4, 14)};
   printArray(arr); // This compiles without complaint! I understand that the
                    // conversion is legal, but it seems like a warning
                    // about this would be a good idea. 
}

注意:我知道这是一个糟糕的设计但是要说明一点。

这里的问题是,当我按照上面的方式混合这两个时,当我们遍历数组进行打印时,我们不会按正确的数量推进数组的元素(即我们移动sizeof(Base)而不是sizeof(D))。这导致输出:

10
0
11
1
12

[Live example.]

(我猜这样调用operator<<可能是UB)。

使用g++ -std=c++1y -Wall -Weffc++ -pedantic main.cpp进行编译时,我没有收到任何警告或错误。

  1. 是否有我可以启用的编译器标志,表示此方案中的警告?
  2. 如果没有,为什么不呢?

3 个答案:

答案 0 :(得分:2)

编译器可以进行大量静态分析,并且可以知道函数中的指针arr被用作具有意外结果的数组。

然而,这样做很慢并且使用了很多(更多)内存,并且程序员通常不耐烦并且希望尽可能快地使用尽可能少的其他资源来完成编译。因此,大多数编译器只进行相对快速和简单的静态分析,将辛勤工作留给专用静态分析仪。

答案 1 :(得分:1)

void printArray(const Base arr[])相当于void printArray(const Base* arr)

将类型为D的指针传递给参数类型为const Base*的函数是合法的。所以编译器不会发出任何警告。

答案 2 :(得分:-1)

仅供参考,如果将多态性作为句柄类的实现细节提供,则可以完成混合数组和多态性:

#include <iostream>
#include <vector>

// a handle which will happily own any class which implements the concept
struct Thing
{
    struct concept
    {
        virtual void sayHello() const = 0;
        virtual ~concept() = default;
    };

    Thing(std::unique_ptr<concept> ptr) : _impl(std::move(ptr)) {}

    void sayHello() const { _impl->sayHello(); }

    std::unique_ptr<concept> _impl;
};

struct thing_a : Thing::concept
{
    void sayHello() const override { std::cout << "hello from A\n"; }
};

struct thing_b : Thing::concept
{
    void sayHello() const override { std::cout << "hello from B\n"; }
};

int main()
{
    std::vector<Thing> things;

    things.emplace_back(std::make_unique<thing_a>());
    things.emplace_back(std::make_unique<thing_b>());

    for (const auto& t : things) { t.sayHello(); }
}

预期产出:

hello from A
hello from B