2016-04-13 15 views
2

私はPIMPLイディオムと一般的なC++について学んでいます。値を印刷するときにガベージ値が表示されるのはなぜですか?

私は、PIMPLスタイルのインターフェイスを持つクラスを持っています。これは、int値を7に設定します。しかし、印刷するときにガベージ値が得られます。

コード

Test.cppの

#include <iostream> 
#include "Test.h" 
struct Foo::Bar 
{ 
    int value; 
}; 

Foo::Foo() 
{ 
    Bar tempBar; 
    myBar = &tempBar; 
    myBar->value = 7; 
} 
void Foo::printValue() 
{ 
    std::cout << "Value = " << myBar->value << std::endl; 
} 
int main() 
{ 
    Foo myFoo; 
    myFoo.printValue(); 
    return 0; 
} 

TEST.H

class Foo 
{ 
    private: 
     struct Bar; 
     Bar* myBar; 
    public: 
     Foo(); 
     void printValue(); 
     //~Foo(); 
}; 

出力

値= 2147120498

答えて

4

mybarは、Fooコンストラクタ内のローカル変数へのポインタです。コンストラクタが終了すると、変数はなくなりますが、myBarはまだ古いメモリを指しています。

PIMPLを実装したいので、本当に1つの選択肢があります。 newを使用してmyBarを動的に割り当て、Fooデストラクタでdeleteで解放する必要があります。あなたの場合は

Test.cppの

Foo::Foo() 
{ 
    myBar = new Bar; 
    myBar->value = 7; 
} 

Foo::Foo(const Foo &src) 
{ 
    myBar = new Bar; 
    *myBar = *(src.myBar); 
} 

Foo::~Foo() 
{ 
    delete myBar; 
} 

Foo& Foo::operator=(const Foo &rhs) 
{ 
    *myBar = *(rhs.myBar); 
    return *this; 
} 

TEST.H

class Foo 
{ 
private: 
    struct Bar; 
    Bar* myBar; 
public: 
    Foo(); 
    Foo(const Foo &src); 
    ~Foo(); 

    void printValue(); 

    Foo& operator=(const Foo &rhs); 
}; 

を:あなたはまた、メモリリークするを避けるために、同様Fooにコピーコンストラクタとコピー代入演算子を追加する必要がありますPIMPLを実装していない場合、別の選択肢があります。

Test.cppの

Foo::Foo() 
{ 
    myBar.value = 7; 
} 

void Foo::printValue() 
{ 
    std::cout << "Value = " << myBar.value << std::endl; 
} 

テスト:myBar代わりFooクラスの非ポインタのメンバーであることを確認します。あなたはPIMPLをこの方法で行うことができ++近代的なCを使用して時間

class Foo 
{ 
private: 
    struct Bar 
    { 
     int value; 
    }; 

    Bar myBar; 

public: 
    Foo(); 
    void printValue(); 
}; 
2

tempBarは、ローカル変数であるためコンストラクタの最後にスタックからポップされているため、未定義の動作が発生しています。

おそらく、デストラクタでnewdeleteを使用します。

+1

それとも、 'Foo'クラスの'バーmyBar'に '*バーmyBar'を変更し、' myBar.value'の代わりに使用します.cppファイル内

体は可能性があり'myBar-> value'です。 –

+0

ああ、私は何をしたのか分かります。実際にスタックにオブジェクトを置こうとしていました。ヒープを必要とせずに構造化する方法はありますか? – Rethipher

+0

スタックとヒープのみがあり、他には何もありません。しかし、 'Bar myBar'を' Foo'クラスのメンバにすることができます。 'new'を使って割り当てることができます。 –

2

一時的なアドレスを保存しています。

Foo::Foo() 
{ 
    myBar = new Bar; 
    myBar->value = 7; 
} 

をそして、あなたは、三つのルールに従ってデストラクタを提供し、コンストラクタをコピーする必要があります。その代わり、あなたがする必要がそれを割り当てます。

+0

また、 'Foo'クラスの' Bar * myBar'を 'Bar myBar'に変更し、' myBar-> value'の代わりに 'myBar.value'を使用してください。 –

+0

しかし、それは_pimpl_ではありません。そのイディオムの全体的なポイントは、実装の詳細を隠すことです。 – paddy

+0

変更を加えることがPIMPLではないことを説明できますか? – Rethipher

3

#include <memory> 

class Foo 
{ 
    struct Bar; 
    std::unique_ptr<Bar> myBar; 
public: 
    Foo(); 
    ~Foo(); 
    void printValue(); 
};  

unique_ptrが不完全なタイプで動作するいくつかの標準ライブラリコンテナの一つです。この方法の利点は、デストラクタをコメントアウトする、またはFooをコピーしようとするなどの間違いを犯すと、コンパイラがそれをキャッチすることです。

Foo::Foo(): myBar(new Bar) { myBar->value = 7; } 
Foo::~Foo() {} 
関連する問題