なぜmake()またはnew()をするのですか?


203

導入文書は間の違いを説明するために多くの段落を捧げるnew()make()、実際には、ローカルスコープ内のオブジェクトを作成し、それらを返すことができます。

なぜペアのアロケータを使用するのですか?

回答:


170

あなたが行うことができます物事makeあなたが他の方法で行うことができません。

  • チャンネルを作成する
  • スペースが事前に割り当てられたマップを作成する
  • スペースが事前に割り当てられているか、len!= capでスライスを作成します

正当化するのは少し難しいnewです。それが簡単にする主なことは、非複合型へのポインタを作成することです。以下の2つの関数は同等です。もう少し簡潔です。

func newInt1() *int { return new(int) }

func newInt2() *int {
    var i int
    return &i
}

41
「新規」を使用してチャネルを作成することはできません。しかし、私の意見では、要点は、「new」と「make」が1つの組み込み関数に結合されるとどうなるでしょうか。確かに、そのような交換は可能です。可能であるので、問題は、1つの一般化された組み込み関数ではなく、2つの組み込み関数を持つ客観的な理由は何ですか?-あなたの答えは、「new」を使用してチャネル/マップ/スライスを作成することはできないと正しく述べていますが Goが「new」と「make」を持っている理由を1つの一般化されたalloc + init関数ではなく正当化していません。

5
これらは組み合わせることができ、Rob Pikeによって一度に提案されました:groups.google.com/d/topic/golang-nuts/kWXYU95XN04/discussion。結局、あなたの答えで与えられたものと同様の理由でそれは通り抜けませんでした。
エヴァンショー

12
効果的なgoは、newがゼロの値を返すのに対し、mapはゼロ以外のタイプのマップ、スライス、またはチャネルを割り当てるという点です。golang.org/doc/effective_go.html#allocation_new
kristianp

どの程度m := map[string]int{}の代わりにm := make(map[string]int)?サイズを事前に割り当てる必要もありません。
Noam Manos

165

Goには、メモリの割り当てと値の初期化に複数の方法があります。

&T{...}&someLocalVarnewmake

割り当ては、複合リテラルを作成するときにも発生します。


new整数などの値を割り当てるために使用できますが、これ&intは不正です。

new(Point)
&Point{}      // OK
&Point{2, 3}  // Combines allocation and initialization

new(int)
&int          // Illegal

// Works, but it is less convenient to write than new(int)
var i int
&i

違いnewとは、make次の例を見て、見ることができます。

p := new(chan int)   // p has type: *chan int
c := make(chan int)  // c has type: chan int

Goにはnewand がなくmake、組み込み関数があるとしますNEW。次に、コード例は次のようになります。

p := NEW(*chan int)  // * is mandatory
c := NEW(chan int)

* 必須になりますので、:

new(int)        -->  NEW(*int)
new(Point)      -->  NEW(*Point)
new(chan int)   -->  NEW(*chan int)
make([]int, 10) -->  NEW([]int, 10)

new(Point)  // Illegal
new(int)    // Illegal

はい、マージnewmakeシングルビルトイン関数にすることは可能です。ただし、1つの組み込み関数を使用すると、2つの組み込み関数を使用するよりも、新しいGoプログラマーの間で混乱が生じる可能性があります。

上記のすべての点を考慮すると、分離する方が適切であるnewmake考えられます。


@TorstenBronger私は読みやすく、新しいことがわかり、それintが作成されたインスタンスであることを示しています。
Daniel Toebe

4
あなたは、書き込みにもしかしてmake(Point)及びmake(int)それらの最後の2行には?
Jimmy Huch、

27

makefunctionは、slice、map、またはchanタイプのオブジェクトのみを割り当てて初期化します。のようnewに、最初の引数は型です。ただし、サイズという2番目の引数を取ることもできます。newとは異なり、makeの戻り値の型は、引数へのポインタではなく、引数の型と同じです。そして、割り当てられた値は初期化されます(newのようにゼロ値に設定されません)。その理由は、slice、map、chanがデータ構造であるためです。これらは初期化する必要があります。そうしないと使用できません。これがnew()とmake()が異なる必要がある理由です。

次のEffective Goの例は、非常に明確です。

p *[]int = new([]int) // *p = nil, which makes p useless
v []int = make([]int, 100) // creates v structure that has pointer to an array, length field, and capacity field. So, v is immediately usable

1
ではnew([]int)、単に[] intにメモリを割り当てますが、初期化は行わないため、単に戻りますnil。使用できないため、メモリへのポインタではありません。make([]int)使用できるように割り当てて初期化し、そのアドレスを返します。
o0omycomputero0o

12
  • new(T)-割り当てメモリ、およびに設定、それをゼロ値タイプのT ..
    ある..that 0ためINT""のための文字列nil参照型(のためのスライスマップチャン

    参照される型は、いくつかの基になるデータ構造へのポインタにすぎないことに注意してください。これnew(T)
    では作成さません。スライスの場合、基になる配列は作成されないためnew([]int) 、ポインタは何も返しません。

  • make(T)-参照されるデータ型(slicemapchan)にメモリを割り当て、その基礎となるデータ構造を初期化ます

    例:sliceの場合、基になる配列は指定された長さと容量で作成されます
    。Cとは異なり、配列はGo!のプリミティブ型であることに注意してください。


言われていること:

  • make(T) 複合リテラル構文のように動作します
  • new(T)のように動作varします(変数が初期化されていない場合)

    func main() {
        fmt.Println("-- MAKE --")
        a := make([]int, 0)
        aPtr := &a
        fmt.Println("pointer == nil :", *aPtr == nil)
        fmt.Printf("pointer value: %p\n\n", *aPtr)
    
        fmt.Println("-- COMPOSITE LITERAL --")
        b := []int{}
        bPtr := &b
        fmt.Println("pointer == nil :", *bPtr == nil)
        fmt.Printf("pointer value: %p\n\n", *bPtr)
    
        fmt.Println("-- NEW --")
        cPtr := new([]int)
        fmt.Println("pointer == nil :", *cPtr == nil)
        fmt.Printf("pointer value: %p\n\n", *cPtr)
    
        fmt.Println("-- VAR (not initialized) --")
        var d []int
        dPtr := &d
        fmt.Println("pointer == nil :", *dPtr == nil)
        fmt.Printf("pointer value: %p\n", *dPtr)
    }

    プログラムを実行する

    -- MAKE --
    pointer == nil : false
    pointer value: 0x118eff0  # address to underlying array
    
    -- COMPOSITE LITERAL --
    pointer == nil : false
    pointer value: 0x118eff0  # address to underlying array
    
    -- NEW --
    pointer == nil : true
    pointer value: 0x0
    
    -- VAR (not initialized) --
    pointer == nil : true
    pointer value: 0x0


    参考 資料https : //golang.org/doc/effective_go.html#allocation_new https://golang.org/doc/effective_go.html#allocation_make


  • 例を用いて、物事はより明確になります。賛成投票:)
    Sumit Jha

    8

    make()チャネルとマップ(およびスライスを作成する必要がありますが、配列から作成することもできます)。これらを作成する代替方法がないためmake()、レキシコンから削除することはできません。

    についてはnew()、構造体構文を使用できるときになぜそれが必要なのか、その理由がわかりません。ただし、「すべてのフィールドがゼロ値に初期化された構造体を作成して返す」という独特のセマンティックな意味を持つため、便利です。


    1
    だから、新しい避け、ちょうど構造体の構文を使用することを好むべきである
    CommonSenseCode

    8

    別にすべてからで説明した効果的な移動の間の主な違いnew(T)&T{}、後者は、明示的にヒープ割り当てを行うことです。ただし、これは実装に依存するため、変更される可能性があることに注意してください。

    2つはまったく異なる機能を実行するmakeためnew、比較しても意味がありません。しかし、これはリンクされた記事で詳細に説明されています。


    10
    &T{}ヒープ割り当てを明示的に実行するという主張は、仕様の何にも基づいていないAFAIKです。実際、私はエスケープ分析が、可能な場合とまったく同じ方法で、可能な場合は常にそのような* Tをスタックに保持していると考えていnew(T)ます。
    zzzz

    6

    new(T):タイプTへポインタとタイプ* Tの値を返し、メモリを割り当ててゼロにします。new(T)は&T {}と同等です。

    make(T):T 型の初期化された値を返し、メモリを割り当てて初期化します。スライス、マップ、チャネルに使用されます。

    弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
    Licensed under cc by-sa 3.0 with attribution required.