2012-02-03 28 views
0

私はいくつかの一般的なタイプのコンテナを「ラップする」ために使用しているクラステンプレートを持っています。現在、このようになります私はプレースメントの新しい&仮想関数を使用しています。なぜ私の仮想関数テーブルが間違っていますか?

getItems(Cont<ItemType> x); 

削ぎ落とした実装では、(私はベクトルのコードのみが含まれます):目標は、次のようになり、単一の機能を

getItems(vector<ItemType> &x); 
getItems(set<ItemType> &x); 
getItems(my_custom_vector<ItemType> &x); 
(...and several others...) 

を交換することである

template< class T > ContBase { 
public: 
    ContBase(void *container) : mContainer(container) {} 
    virtual void add(const T& item) = 0; 
    virtual void clear() = 0; 
protected: 
    void *mContainer; 
}; 

template< class T > ContVector : public ContBase<T> { 
public: 
    ContVector(std::vector<T> &native_container) 
     : ContBase((void *) &native_container) {} 
    void add(const T& item) { vec().push_back(item); } 
    void clear() { vec().clear(); } 
protected: 
    std::vector<T> &vec() { return *(std::vector<T> *) mContainer; } 
}; 

template< class T > class Cont { 
public: 
    Cont(std::vector<T> &x) { new (&mMem) ContVector<T>(x); } 
    void clear() { container()->clear(); } 
    void add(const T& item) { container()->add(item); } 
protected: 
    ContBase<T> *container() { return (ContBase<T> *) &mMem[ 0 ]; } 
    unsigned char mMem[ sizeof(ContBase<T>) ]; 
}; 

私はいくつかの初期テストを行いました。制御されたベンチマークセットアップでうまくいきました。ここの主な難題は、の賢明なのContのコンストラクタでのその配置の使用です。しかし、いったん実際のアプリケーション(Visual Studio 2010 C++コンパイラ)にスタックして全体プログラムの最適化(リンクタイムコード生成、速度最適化など)を実行すると、重大な問題が発生し始めました。

VFPTR: 0AD98708 
// Repeat above line 17 times 
VFPTR: 0018B830 
:私は、この(非常識な型キャストは、VC++での仮想関数テーブルの値を取得するためのハックであることに注意してください)のような出力を提供します

void MyClass::myFunc(Index x, Cont<Index> container, uint r) 
{ 
    // ... Return if x is invalid 
    cerr << "VFPTR: " << (void*) ((uint *) ((void*) &verts))[0] << endl; 
    container.add(x); 
    // ...Potentially add some more things to container. 
} 

:具体的には、私はこのような機能を持っています

まず、仮想関数ポインタと異なる可能性があります。第2に、この同じコードがデバッグモードで動作するのは面倒です。最適化がオンになっている場合にのみ表示されます。第3に、新しいポインタ(0018B830)がベースクラスの仮想関数テーブルへのポインタであることは注目に値する。ContBase。クラッシュは、NULL値であるcontainer.add(x)を呼び出すと発生します。

私の中核的な質問は、ここにあります:この法的コードですか、または私は未定義の行動事例を見逃しましたか?マイクロソフトのコンパイラはここで壊れていますか、または私のコードは未定義ですか?

+0

プレースメントのnew、 'void *'などを使って大規模に複雑化していると思うのですが、 'getItems'関数を引数に対してパラメータ化したテンプレートにするのはなぜですか? – templatetypedef

+0

オフトピックですが、重要だと思います。 'unsigned char mMem [sizeof(ContBase < T >)];'は、 'ContBase < T >'に対して正しく整列されません。 –

+0

'ContBase <>'はコンストラクタに渡された 'std :: vector <> 'に_pointer_を格納します。 'MyClass :: myFunc'が実行されている時点で' std :: vector <> 'がまだ生きていることを確かめますか?それが削除されたり範囲外になったりすると、単純なオブジェクトライフタイム関連のUBがここにあります。 (あなたがBoost.Variantの観点から 'ContBase <>'を実装した場合、あなたの実装は**大幅に**簡素化されます(http://www.boost。 – ildjarn

答えて

0

私が見る主な未定義の動作の問題は、Cont<T>の(デフォルトの)コピーコンストラクタと代入演算子がmMem配列に保持されているContVector<T>オブジェクトのバイトコピーを実行することです。 ContVector<T>とはっきりコピーできませんので、これは不確定です。(C++仕様の9と3.9を参照)。正確な問題の説明から、オプティマイザはmMem配列のバイトコピーを使用して一部の操作を並べ替えることができると仮定しているように見えます。この結果、部分的に構築された(または部分的に破壊された)オブジェクトのコピーが得られます。

したがって、mMem配列のバイナリコピーに頼るのではなく、コピー/割り当てを正しく行うコピーctorと代入演算子を追加することで、この作業を行うことができます。

+0

Contveector はどのようにして簡単にコピーできないのですか?私の希望するコピー動作はポインタの割り当てです(ContVector <>は既存のベクトルを参照し、それは所有しておらず、コピーは同じものを参照する必要があります)。残念なことに、C++の仕様はオンラインではないので、私はそのセクションを手放すことはできません。 – AHelps

+0

悲しいことに、これは答えとして自動的に選択されましたが、正しくありません。望ましい動作は、実際にはmMemのbit-for-bitコピーです(ContVector はPODです)。あなたが後で来るなら、より正確な答えのために元の質問のコメントをチェックしてください。これはバイトアライメントの問題であったようです。 – AHelps

関連する問題