2016-09-02 2 views
5

В более эффективном 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. Если нет, то почему бы и нет?
+0

Ну, ответ на вопрос «2» означает, что «предупреждения» не являются обязательными для реализации компилятором. Вы можете попросить разработчиков g ++ добавить такое предупреждение (если оно еще не существует), но они не вынуждены делать это по любому стандарту. – PaulMcKenzie

+1

@PaulMcKenzie, правда, но GCC идет выше и дальше с их предупреждениями, мне интересно, есть ли техническая трудность в обнаружении этого? –

+0

Вы должны спросить инженеров, которые собрали компилятор. Там ответ может быть где угодно: «это хорошая идея», чтобы «у нас нет времени», «он уже существует». – PaulMcKenzie

ответ

1

void printArray(const Base arr[]) равнозначно void printArray(const Base* arr).

Законно передавать указатель типа D функции, параметр которой имеет тип const Base*. Поэтому компилятор не даст никаких предупреждений.

+1

Составители могут давать предупреждения для юридических конструкций. – jaggedSpire

+0

Я знаю, что это законное поведение, поэтому 'int arr [5]; arr [6] = 10', мы получаем предупреждение за это. –

+0

@Ben Предположим, что 'printArray' определен в третьей библиотеке, и нет вызова функции для передачи указателя типа' D' в 'printArray' в этой библиотеке. Компилятор ничего не будет жаловаться. Однако, когда кто-то использует библиотеку и передает указатель типа 'D' на' printArray'. Я не думаю, что это хорошая идея для компилятора проверить реализацию 'printArary' в третьей библиотеке, чтобы увидеть, требует ли' printArray' массива типа 'Base'. –

-1

FYI, смесительные массивы и полиморфизм могут быть сделаны, если полиморфизм представлен в качестве деталей реализации класса ручки:

#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 
+2

Как это ответить на вопрос? – Barry

+1

@Barry Я думаю, что цель этого сайта - ответить на вопросы * и * для обучения. Дело в том, что полиморфизм может быть совместим с векторной защитой, если сделать это безопасно. Это один из способов сделать это безопасно. Ответ в духе образования. –

2

компилятора может сделать много статического анализа и может знать, что указатель arr в функции используется как массив с неожиданными результатами.

Однако выполнение этого происходит медленно и использует много (больше) памяти, а программисты, как правило, нетерпеливы и хотят, чтобы их компиляции выполнялись как можно быстрее, используя как можно меньше других ресурсов. Поэтому большинство компиляторов делают только статические анализы, которые являются относительно быстрыми и легкими, оставляя тяжелую работу специализированным статическим анализаторам.

+0

Так я предполагаю, что вы думаете, что ответ на 1) нет. –

+0

@Ben Наверное нет. Если в документации не упоминается такое предупреждение, то единственный способ узнать наверняка - получить источник (если он доступен) и прочитать его. :) –

+0

Я действительно не покупаю аргумент замедления компиляции, так как предупреждения обычно можно отключить. Таким образом, любой, кто обеспокоен безопасностью, может включить их, и все, кто обеспокоен скоростью компиляции, могут оставить их – user463035818

Смежные вопросы