2009-04-06 10 views
1
#include <iostream> 
using namespace std; 

class Foo 
{ 

public: 

Foo(): initialised(0) 
{ 
    cout << "Foo() gets called AFTER test() ?!" << endl; 
}; 

Foo test() 
{ 
    cout << "initialised= " << initialised << " ?! - "; 
    cout << "but I expect it to be 0 from the 'initialised(0)' initialiser on Foo()" << endl; 
    cout << "this method test() is clearly working on an uninitialised object ?!" << endl; 
    return Foo(); 
} 

~Foo() 
{}; 

private: 

int initialised; 

}; 


int main() 
{ 

//SURE this is bad coding but it compiles and runs 
//I want my class to DETECT and THROW an error to prevent this type of coding 
//in other words how to catch it at run time and throw "not initialised" or something 

Foo foo=foo.test(); 

} 

答えて

1

実際にコード化されていない人がになるのを防ぐことはできません。フー::テストを(この):

  1. 実行してはFoo ::テストに行く
  2. (「この」ポインタの値である)はFooのメモリを割り当て:それはちょうどそれが「はず」のように動作します(なぜなら、戻りはFoo()の;)、
  3. 、ここでそれはそれは
  4. はフーのデフォルトコンストラクタを呼び出し、ランダムなジャンクで初期化this-によって値>を、取得
  5. コールのFooのコピーコンストラクタ、その後、右利きのFoo()をコピーします。

ちょうどそうであるように。人々がC++を使う正しい方法を知らないのを防ぐことはできません。値が既に1337です_magicFlagが割り当てられます可能性が低いため

class A 
{ 
public: 
    A(void) : 
    _magicFlag(1337) 
    { 
    } 

    void some_method(void) 
    { 
     assert (_magicFlag == 1337); /* make sure the constructor has been called */ 
    } 

private: 
    unsigned _magicFlag; 
} 

この「作品」:あなたができる

最高のマジックナンバーを持っています。

本当に、これをしないでください。

+0

あなたは純粋です:) C++はマルチレベル言語として請求されており、アプリケーションプログラマとシステムプログラマを使っています。クラスが誤って使用されるのを防ぐために、多くの時間が費やされています。あなたの魔法の数字は私の解決策です:)) –

+0

= |私はまだ魔法の数のアイデアに強く反対しています。 :O 多分あなたが何を防止しようとしているのかを詳述すれば、それをより適切に処理する方法があります。 – GManNickG

+0

:)私は非C++プログラマが以下のようなプログラミングにC++を使用できるようにする "var"というクラスを定義しました。 var aa = 1; var bb = 2; print(aa + bb); さらに、私たちの事業に関連する特別な機能が満載です。 –

5

はい、未定義の動作で、未構成のオブジェクトで関数を呼び出しています。あなたは信頼できるものを検出することはできません。私はあなたもそれを検出しようとすべきではないと主張します。例えば、すでに削除されたオブジェクトの関数を呼び出す場合とは異なり、偶発的に起こる可能性はありません。起こりうるすべての間違いをキャッチしようとするのは不可能です。宣言された名前は、他の便利な目的のために、イニシャライザにすでに表示されています。これを考慮してください:

Type *t = (Type*)malloc(sizeof(*t)); 

これはCプログラミングでよく使われるイディオムで、C++でも動作します。

個人的には、私は012bのHerb Sutterがヌル参照について(同様に無効です)好きです。要点は、言語がはっきりと禁じられており、特に一般的なケースでは確実に診断することが不可能であるという事例から保護しようとしないことです。時間が経つにつれて偽の安全保障を受けることになるため、非常に危険です。代わりに、間違いの可能性を減らす方法で(生のポインタを避けて...)、言語とデザインインターフェイスの理解を養成してください。

C++と同様に、多くのケースは明示的に禁止されていませんが、未定義です。部分的には、を効率的に診断するのが難しく、未定義の動作では、実装を完全に無視するのではなく、代わりに既存のコンパイラによって使用されるように設計することができます。

上記の例では、任意の実装で自由に例外をスローすることができます。実装のために効率的に診断するのがはるかに難しい、未定義の動作である他の状況があります。オブジェクトが構築される前にアクセスされた別の変換ユニットにあることは、静的初期化順序の失敗であるとして知られています。

+0

+1、好奇心。この特定のケースでコンパイルエラーが発生した場合でもコンパイラーは標準のままです(このタイプのエラーを一般的に確実に診断する可能性は無視されます)。 – JaredPar

+0

明らかに間違っているので、MSVCもGCCもこのタイプの誤ったコーディングについて警告を出すことはありません。 –

+0

JaredPard、thanks :)はい、未定義の動作を含むプログラムのコンパイルに失敗する可能性があります。コンパイラは、それを実行しているシステムをクラッシュさせることを含め、好きなものすべてを行うことができます:)(再帰的なテンプレートのインスタンス化があったときに私に起こった - スワッピングで狂った!) –

2

コンストラクタは必要なメソッドです(初期化の前に実行されているのではなく、初期化されていますが、これは問題ありません)。あなたのケースではうまくいかない理由は、ここで未定義の動作があることです。

特に、それ自体を初期化するには、まだ存在しないfooオブジェクトを使用します(例えば、foofoo.Test()に存在しないなど)。あなたがプログラムの中でそれを確認することができません

Foo foo=Foo().test() 

、多分valgrindのは、(他の初期化されていないメモリアクセスなど)のバグのこのタイプを見つけることができる:あなたは、明示的にオブジェクトを作成することによって、それを解決することができます。

0

あなたは基本的に「コンパイラがあなたにこれを手伝ってくれると期待してはいけない」というかなりの回答を得ています。しかし、コンパイラが何らかの診断でこの問題の解決に役立つはずだと私は同意します。残念ながら(他の答えが指摘しているように)、言語仕様はここでは役に立ちません。宣言の初期化部分に到達すると、新しく宣言された識別子が有効範囲に入ります。

  • 使用してゴミ
  • で、オブジェクトのメモリを上書き消去
  • 後のオブジェクトを使用して:

    Aは戻っている間、DDJはを支援するデバッグ支援として使用することができ、その記事についてa simple debugging class called "DogTag"を持っていましたしかし、それはいくつかのメモリの上書きBに実行していた埋め込まれたプロジェクトでhandlyに来た - オブジェクトそれは

私はあまりそれを使用していない初期化する前に、 ugs。

基本的にはthe "MagicFlag" technique that GMan describedの精緻化です。

+0

ありがとうございます。 Dogtagのアイデアは、非常に敵対的な環境で役に立つと思われます。メモリを捨てる –

関連する問題