2016-09-02 5 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'が第3のライブラリで定義されていて、そのライブラリの' printArray'に 'D'型のポインタを渡す関数呼び出しはないとします。コンパイラは何も文句を言うことはありません。しかし、誰かがライブラリを使用し、 'printArray'に' D'型のポインタを渡します。 'printArray'が' Base'型の配列を必要としているかどうかを調べるために、第3のライブラリの 'printArary'の実装をコンパイラがチェックするのは良い考えです。 –

-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私はこのサイトの目的は教育のための質問*と*に答えることだと思います。事実、多形性は、安全に行われた場合、ベクトル封じ込めと互換性があります。これは安全に行う方法の1つです。答えは教育の精神です。 –

2

関数内のポインタarrが予期しない結果を持つ配列として使用されていることがわかりました。

しかし、これを行うのは時間がかかり、メモリが大量になります。プログラマは一般的にせっかちで、できるだけ早くできるだけ早くコンパイルを行いたいと考えています。したがって、ほとんどのコンパイラでは比較的迅速かつ簡単な静的解析しか行われず、専用スタティックアナライザには苦労しています。

+0

だから私は1)への答えはいいえだと思います。 –

+0

@Benおそらくいいえ。ドキュメントにそのような警告が記載されていない場合、確実に知る唯一の方法はソースがあればそれを入手して読むことです。 :) –

+0

警告は通常オフにすることができるので、私は実際にコンパイルを遅くするという議論を買っていません。だから、安全性を心配している人は誰でもファイルを有効にして、コンパイルの速度を心配している人は誰でもファイルを残すことができます – user463035818

関連する問題