4

次の宣言は、C++ファイルでコンパイルするための演算子をいくつか追加します。定義は、CファイルとC++ファイルの両方に含まれています。

PC-Lintがエラー114を報告しています:タグ'Rect'の構造宣言に一貫性がありませんが、安全であると確信しています。

VisualStudio2008でコンパイルしています。

編集-クライアントに送信した説明を追加します

Rectの問題に関して; 構造体がCとC++で同じサイズであることを知ることで、「未定義動作」の疑いをどのように排除できますか。

データ構造内のフィールドの実際の場所がコンパイルによって異なる場合、未定義の動作が発生します。

すべてのメンバー変数アクセスは、最終的にはポインターに解決されると考える必要があります。これは、オブジェクトストレージの先頭へのポインターと、その構造内にあるものに応じたオフセットによって計算されます。

パッキングとデータアライメントの設定は、オフセットの値に影響します。

コンパイラーは、最適なアクセスのために型を並べ替えることができます。指定された順序で2つのメンバーを宣言したからといって、実際にはその順序で格納されていると想定するのは未定義の動作です。宣言順序が保証するのは、初期化、コピー、および破棄の順序だけです。

ただし、同じコンパイラ内で同じオフセット設定を使用して特定の構造体をCおよびC ++でコンパイルする場合、実際の並べ替えの可能性は事実上ゼロです。

したがって、心配する必要があるのは、フィールドのオフセットの違いだけです。

単純な4つの短整数を含む構造体の場合、CバージョンとC ++バージョンが同じサイズであることを確認するだけで、それらのオフセットがすべて同じであることが保証されます。さらに注意するために、構造体のサイズ= 4 * sizeof(short)であることを確認することもできます。

これらのチェックを追加する価値があると思いますが、一度追加すると、CとC ++で別々の型を使用する(または使用されている関数を無料の関数に移動する)ために必要なコードをリファクタリングする必要はありません。

/**
Mac-compatible rectangle type with some operators added for C++ use.
@ingroup QuickdrawPort
*/
struct Rect {
    short                           top;
    short                           left;
    short                           bottom;
    short                           right;

#ifdef __cplusplus
    Rect(short _top=0, short _left=0, short _bottom=0, short _right=0) :
        top(_top),
        left(_left),
        bottom(_bottom),
        right(_right)
    {}
    #ifdef _WINNT_  // WinDef.h has been included
        const Rect& operator=(const tagRECT& rhs) {
            top = short(rhs.top);
            left = short(rhs.left);
            bottom = short(rhs.bottom);
            right = short(rhs.right);
            return *this;
        }

        operator tagRECT() const {
            tagRECT lhs;
            lhs.top = top;
            lhs.left = left;
            lhs.bottom = bottom;
            lhs.right = right;
            return lhs;
        }

    #endif// _WINNT_
    short height() const { return bottom - top; }
    short width() const { return right - left; }
    bool empty() const { return right==left || bottom==top; }

    bool operator==(const Rect& rhs) const {
        return top == rhs.top &&
            left == rhs.left && 
            bottom == rhs.bottom &&
            right == rhs.right;
    }
#endif  
};
#ifndef __cplusplus
typedef struct Rect Rect;
#endif
4

1 に答える 1

7

この定義を含むヘッダー ファイルがいくつかの翻訳単位に含まれており、そのうちのいくつかは C++ としてコンパイルされ、一部はプレーン C としてコンパイルされていると仮定するのは正しいですか?

true の場合は、ODR違反があり、C++ 標準に従って、未定義の動作を引き起こします。

それに対処するには2つの方法があります。

  1. サポートする必要があるプラットフォームでどのように現れるかを正確に知っている場合は、未定義の動作のこのインスタンスを無視してください。(未定義の動作は、正しく動作するプログラムとして現れる可能性があることに注意してください)。実を言うと、ほとんどのコンパイラでは、ここで問題が発生することはありません。ただし、このコードは法律ではなく偶発的に機能することを理解する必要があります。コンパイラは、メンバー関数を使用するクラスと、メンバー関数を使用しないクラスで異なるレイアウトを使用できます。(たとえば、このコードがCINTで壊れることは間違いありません)。

  2. 未定義の動作を排除します。この道を行くことをお勧めします。それを行うにはいくつかの可能性があります。たとえば、"C Rect" から "C++ Rect" を継承し、後者をプレーンとして保持できますstruct

于 2011-02-28T11:49:49.057 に答える