2017-06-02 12 views
1

"foo"という型の要素を保持するメンバーベクトルを含むクラス "foo"があります。このクラスには "make"というメソッドがあり、 "foo"オブジェクトを作成してベクトルに追加します。私はまた、 "foo"オブジェクトをナビゲートする方法を提供し、 "foo"ベクトル要素は "get"と呼ばれます。"* this"が参照するものを変更するC++クラス

どの "foo"オブジェクトでも "this"が参照しているものをベクトル内のfooオブジェクトを指すように再割り当てしたいと思います。私は、特定の "foo"メンバーへの参照をバインドせずに、fooオブジェクトをより効率的にナビゲートして追跡する方法として、この機能をコードに与えたいと思います。私は "* this"が何かを再割り当てする "setAsNode"というメソッドを通してこれをやろうとしています。

ここで私は、私は全体の私のポイントを取得すると信じているアップ嘲笑いくつかのサンプルコードです:私はおそらく実装してる実感

foo f1("test1"); 
f1.make("test2").make("test3");//pushes a new foo element back to the vector in f1 and pushes a new foo element back to it's vector 
f1.get(0).get(0);//refers to the deepest nested object we just made above, cumbersome to type especially if the nesting is deep 
f1.get(0).get(0).setAsNode();//this is where the error occurs, the desired effect is to make f1 the same object as denoted by f1.get(0).get(0); 

foo& f2 = f1.get(0).get(0);//f2 would be equivalent to what I want the above code to reset f1 to be (or at least how I'd like it to act) 

struct foo{ 
    foo(const std::string &r):s(r){} 
    foo& make(const std::string &r){children.push_back(test(r)); children.back().originalNode = originalNode; return children.back();} 
    foo& setAsNode(){*originalNode = *this; return *originalNode;} 
    foo& get(int i){return children.at(i);} 
private: 
    std::vector<foo> children; 
    std::string s = "someData"; 
    foo *originalNode = this; 
}; 

、これが機能するかの具体例は、 "foo"オブジェクトを返すためのポインタの代わりに参照を使用するなどの非常に悪いプログラミング慣行がありますが、正にこのプログラムを正しく構成する方法や、このプログラムのベストプラクティスバージョンがどのように見えるか、わかりません。私に物事を「正しい」方法で示すことができる人のためのボーナスポイント。

実際の質問に戻る:私はこのようなもの、具体的には "setAsNode"メソッドを実際に動作させるにはどうしたらいいですか?また、私の例のコードはなぜ機能しないのですか?コンパイルは正常に実行されるとクラッシュします。

+1

あなたがSTD 'のコンテナを使用する場合は:: unique_ptrを'代わりに、あなたはそれぞれの 'T'のアドレスが変更されていないことを保証することができます。 'unique_ptr'だけが動きます。また、より有利な参照無効化ポリシーを持つコンテナの使用を検討することもできます。例えば、 'std :: list'は問題の要素がリストから削除されたときだけ参照を無効にします。 –

+3

タイトルの質問に直接答えるには、「this」が別のインスタンスを指すようにする方法がありません。せいぜい、別のインスタンスのメソッドを呼び出すことができます。ここでは、 'this'は前のコンテキストとは' this'と異なります。 –

+0

あなたが必要と主張するものがなぜ必要なのか理解できません。 'originalNode'と' setAsNode'を削除すると、あなたのサンプルのほとんどすべてがうまく動作します。 –

答えて

0

例では、foo.get(0).get(0).setAsNode()を呼び出すと、foo.get(0).get(0)の値がfooにコピーされます。このプロセスでは、foo.childrenに新しい値が割り当てられ、ベクターにはそれ以前の要素が消去されて、foo.get(0).get(0)が破棄されます。これは、thisが破壊されており、ポインタを使用できないことを意味します。しかし、これは現在私たちが現在thisを使用している割り当て操作中に起こっています。これを解決するには、コピーされる値がコピーされるのに十分長く続くことを保証する必要があります。直感的な解決策は、割り当てる前に割り当てる値をコピーすることです。これは動作します

foo& setAsNode() { 
    auto this_copy = *this; 
    *originalNode = std::move(this_copy); 
    return *originalNode; 
} 

、あなたはまだ*originalNodeに割り当てた後thisを使用しないために決して注意する必要があります。別の解決策は、割り当てを実行する前にoriginalNodeの子ベクトルを制御することです。このバージョンでは、thisはメソッドが返るまで有効ですが、次の割り当てが例外をスローすると、ツリーは無効な状態になります。

foo& setAsNode() { 
    auto original_vect = std::move(originalNode->children); 
    *originalNode = *this; 
    return *originalNode; 
} 

私は、オブジェクトを自殺させるようなデザインが気になることがあります。これは、オブジェクトが自分の所有権を管理していること、または所有権の責任が別途周期的であることを意味します。

2

C++の方法(およびおそらく正しい方法のみ)はの別の関心事です。

fooはfoo-finderではありません。それはfoo-thingsを行うべきであり、foo-navigationのものを行うべきではありません。

fooのカーソルまたはイテレータとして機能する新しいクラスを作成します。

ここでは、foo_cursorがfooスタックを通じた移動を覚えている、少し拡張されたバージョンです。あなたの問題はおそらく過剰ですが、foo-navigationロジックをfoo-implementationロジックから分離する原則を実証しています。

これを行うほど、プログラムの作成、デバッグ、および保守が容易になります。

#include <utility> 
#include <string> 
#include <vector> 
#include <stack> 
#include <stdexcept> 
#include <iostream> 


struct foo{ 
    foo(const std::string &r) 
     : children() 
     , s(r) 
    {} 

    foo& make(const std::string &r) 
    { 
     children.emplace_back(r); 
     return children.back(); 
    } 

    foo& get(int i) 
    { 
     return children.at(i); 
    } 

    void print() const { 
     std::cout << s << std::endl; 
    } 


private: 

    std::vector<foo> children; 
    std::string s = "someData"; 
}; 

struct foo_cursor 
{ 
    foo_cursor(foo& f) 
     : current_(std::addressof(f)) 
    {} 

    foo_cursor& down(int i) 
    { 
     history_.push(current_); 
     current_ = std::addressof(current_->get(i)); 
     return *this; 
    } 

    foo_cursor& up() { 
     if (history_.empty()) { 
      throw std::logic_error("went up too far"); 
     } 
     else { 
      current_ = history_.top(); 
      history_.pop(); 
     } 
     return *this; 
    } 

    foo* operator->() const { 
     return current_; 
    } 

private: 
    foo* current_; 
    std::stack<foo*> history_; 
}; 


int main() 
{ 
    foo f("a"); 
    f.make("b").make("c"); 

    auto fc = foo_cursor(f); 
    fc.down(0).down(0)->print(); 
    fc.up()->print(); 
    fc.up()->print(); 
} 

予想される出力:

c 
b 
a 
関連する問題