2013-03-10 22 views
5

私はC++開発の9年後にGoを探検しています。 C++では、パフォーマンスの不利益のために、組み込み型の変数を除いて、関数の引数を値で渡すのは悪い習慣です。引数のすべてのフィールドがコピーされ、ほとんどの場合、非常にコストのかかる操作になります。Goメソッドで "this"を値渡しするとパフォーマンスが低下しますか?

Goではこれは当てはまりますか?メソッドに "const"セマンティックを割り当てるためだけに、値で "this"を渡すのは非常に高額に見えます。最初の変更前に変数がコピーされないようにするには、コンパイラをスマートにしてください。 C/C++のようにGoのアンチパターンを値で渡すのはなぜですか?

+4

あなたのC++の知識は時代遅れです。値渡しは実際には多くの状況で問題ありません(そして、組み込み関数ではないにしても、たくさんの型では常に* OKです)。以下はプライマーです:http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/ –

+0

あなたが心配しているコードを教えてください。 Goでは、ポインタだけで構造体を渡すことができます。コピーが必要な場合は、実際にコピーする関数を作成する必要があります。 – janislaw

+2

@janislawそうでない:http://play.golang.org/p/w5_6_w8JAn – zzzz

答えて

6

Goのthisはreceiverと呼ばれます。はい、非ポインタ受信機のみを使用して "const"セマンティクスをエミュレートするのは非常に高価です。しかし、良い理由から "const"修飾子を削除しました。したがって、不要なコピーを犠牲にして、特定の言語設計の決定を引き継ぐことは、おそらく良い考えではありません。

「this」と「self」と「receiver」の用語の違いは、セマンティクスも異なることを意味します。 IIRCでは、他のいくつかの言語では "this"または "self"の値を変更できませんが、Goではレシーバは別の関数パラメータ(実際にはコンパイラの観点からは最初の関数パラメータ)です。

これは、受信者変数の名前がthisまたはselfであるという記述方法を推奨しない理由です。他の言語に慣れた人にとっては誤解を招きます。私はあなたのC++の知識が(値によって構造体を渡す)関数の引数として高価であるかについてのゴーに罰金翻訳しますと言うでしょう

func (n *node) walk(f func(*node)) { 
     for n != nil { 
       f(n) 
       n = n.next 
     } 
} 
+0

非常に興味深い例 - ありがとう! –

+0

私は受信機がPythonで同じ方法で動作すると信じていますが、慣例ではそれを「自己」とも呼んでいます。私はあなたのレシーバーを "これ"または "自己"と呼ぶのがなぜ混乱するのか理解できません...たとえ誰かがJavaで "this"を扱うようなレシーバーを扱っても、AFAICTは危険です...私はします私のコードを自分自身や他の人のために読みやすくするために、いつもそうしています。 – weberc2

+4

「i int」、「p * T」、「s string」、「f * os.File」、「フラグbool」、「n * node」、「r * root」を検討してください。結果として再宣言されていないと仮定して、すべての変数の名前を 'blah'に変更します。あなたはそれが「より読みやすい」_と主張しますか?私はそう信じていない。すべての受信者にヒントを付けるのではなく、同じ名前に名前を付けることは、わかりやすく、読みにくくなります。それはそれを強制する言語からの唯一の習慣です。しかし、ゴーはしないので、制限が存在しない場合には、自分自身を制限し続ける理由はありません。 – zzzz

5

と何ISN:

うまくいけば、アイデアを示す完全に作らアップの例't(組み込み型、例えばint)。

大きな違いは、リファレンスタイプのスライスmapchannelです。これらは、値渡しのように見えますが(ポインタを使用する必要はありません)、実際には参照渡しとなりますので、一般的にはスライス、マップ、またはチャネルへのポインタを使用しないでください。

も特別です。これらは、フードの参照型ですが、不変でもあり、直接渡します。

thisという特定のケースや、Goで呼ばれる受信機については、同じルールが適用されます(C++とは異なり、組み込みタイプを受信機として使用できることに注意してください)。コンパイラが十分スマートではないと思いますコピーを避けるために、大きな構造体のポインタを使用してください。

8

他の回答は良いですが、私の意見では、いくつかの情報がありません。移動中

レシーバは、以下のcodeによって示されるように、単なる糖衣構文です:

package main 

import "fmt" 

type Something struct { 
    Value int 
} 

func (s *Something) ChangeValue(n int) { 
    s.Value = n 
} 

func main() { 
    o := new(Something)    // o is of type *Something 
    fmt.Println(o.Value)   // Prints 0 
    o.ChangeValue(8)    // Changes o.Value to 8 
    fmt.Println(o.Value)   // Prints 8 
    (*Something).ChangeValue(o, 16) // Same as calling o.ChangeValue(16) 
    fmt.Println(o.Value)   // Prints 16 
} 

これに基づき、ChangeValueの受信機はタイプSomethingの値の代わりにポインタだった場合に何が起こるかを検討しますone ...

そうですよ!この方法で実際にoValueフィールドを変異させることはできません。ほとんどの場合、カプセル化を行うためにポインタ受信機を使用します。

5

受信機のサイズによって異なります。受信者が数十バイト未満の場合は、ポインタを渡した場合に必要となるポインタ追い越し(余分なメモリアクセス)よりも実際には安いかもしれません。また、ポインタを使用すると、構造体がヒープ上に割り当てられる可能性が高くなり、ガベージコレクタに負担がかかります。

Goでは、コピーは常にバイトごとのコピーであるため、コストは構造体のサイズにのみ依存します。 C++では、コピーコンストラクタが呼び出される可能性があります。

本当に大きなオブジェクトを除いて、メソッドのセマンティクスと他のAPIとの一貫性に基づいて、どんな種類の受信機でも最も合理的なものを使用してください。

関連する問題