ทำไมฉันต้องสร้าง () หรือใหม่ ()


203

เอกสารแนะนำให้ใช้หลายย่อหน้าเพื่ออธิบายความแตกต่างระหว่างnew()และmake()แต่ในทางปฏิบัติคุณสามารถสร้างวัตถุภายในขอบเขตภายในและส่งคืนได้

ทำไมคุณถึงใช้ผู้จัดสรรคู่

คำตอบ:


170

สิ่งที่คุณสามารถทำได้ด้วยmakeสิ่งที่คุณไม่สามารถทำได้:

  • สร้างช่อง
  • สร้างแผนที่ที่มีการจัดสรรพื้นที่ไว้ล่วงหน้า
  • สร้างส่วนที่มีช่องว่างที่จัดสรรล่วงหน้าหรือด้วย len! = cap

มันเป็นเรื่องเล็ก ๆ น้อย ๆ newยากที่จะปรับ สิ่งสำคัญที่ทำให้ง่ายขึ้นคือการสร้างพอยน์เตอร์สำหรับประเภทที่ไม่ใช่คอมโพสิต ทั้งสองฟังก์ชั่นด้านล่างเทียบเท่า มีเพียงข้อสรุปเล็ก ๆ น้อย ๆ :

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

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

41
เป็นความจริงที่ไม่สามารถใช้ 'ใหม่' เพื่อสร้างช่อง อย่างไรก็ตามในความคิดของฉันประเด็นคือ: จะเกิดอะไรขึ้นถ้า 'ใหม่' และ 'ทำ' เข้าร่วมในฟังก์ชั่นในตัวเดียว? แน่นอนว่าการเปลี่ยนดังกล่าวจะเป็นไปได้ เนื่องจากมีความเป็นไปได้คำถามคืออะไรคือสาเหตุของการมีฟังก์ชั่นในตัว 2 ฟังก์ชั่นมากกว่า 1 ฟังก์ชั่นทั่วไปในตัว - คำตอบของคุณถูกต้องบอกว่าไม่สามารถใช้ 'ใหม่' เพื่อสร้างช่อง / แผนที่ / ชิ้น แต่ไม่ได้ให้เหตุผลว่าทำไม Go มี 'ใหม่' และ 'ทำ' แทนที่จะมี 1 ทั่วไป + จัดสรรฟังก์ชัน init + init

5
พวกเขาอาจจะรวมกันและมันก็เสนอได้โดยร็อบหอกจุดหนึ่ง: groups.google.com/d/topic/golang-nuts/kWXYU95XN04/discussion ในที่สุดมันก็ไม่ได้ไปด้วยเหตุผลที่คล้ายกับที่ให้ไว้ในคำตอบของคุณ
Evan Shaw

12
การใช้งานอย่างมีประสิทธิภาพทำให้จุดที่ใหม่ส่งคืนค่าศูนย์ในขณะที่แผนที่จัดสรรแผนที่ประเภทที่ไม่เป็นศูนย์ชิ้นหรือช่อง ดูgolang.org/doc/effective_go.html#allocation_new
kristianp

แล้วm := map[string]int{}แทนที่จะเป็นm := make(map[string]int)อะไร ไม่จำเป็นต้อง preallocate ขนาดเช่นกัน
Noam Manos

165

Go มีวิธีการจัดสรรหน่วยความจำหลายวิธีและการกำหนดค่าเริ่มต้น:

&T{...}, &someLocalVar, new,make

การจัดสรรสามารถเกิดขึ้นได้เมื่อสร้างตัวอักษรผสม


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

Suppose Go ไม่มีnewและmakeมีฟังก์ชั่นในNEWตัว จากนั้นโค้ดตัวอย่างจะมีลักษณะดังนี้:

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

The * จะเป็นข้อบังคับดังนั้น:

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

ใช่กลมกลืนnewและmakeเป็นหนึ่งในตัวฟังก์ชั่นเป็นไปได้ อย่างไรก็ตามมีความเป็นไปได้ที่ฟังก์ชั่นในตัวเดียวจะทำให้เกิดความสับสนมากขึ้นในหมู่นักเขียนโปรแกรม Go คนใหม่กว่ามีฟังก์ชั่นในตัวสองตัว

เมื่อพิจารณาจากจุดทั้งหมดข้างต้นแล้วจะมีความเหมาะสมมากกว่าnewและmakeจะแยกออกจากกัน


@TorstenBronger ฉันค้นหาใหม่เพื่อให้อ่านง่ายขึ้นและแสดงว่าเป็นอินสแตนซ์ที่intสร้างขึ้น
Daniel Toebe

4
คุณหมายถึงการเขียนmake(Point)และmake(int)ในช่วงที่ผ่านมา 2 สาย?
Jimmy Huch

27

makeฟังก์ชั่นการจัดสรรและเริ่มต้นวัตถุประเภทชิ้นแผนที่หรือจังเท่านั้น เช่น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)- จัดสรรหน่วยความจำสำหรับชนิดข้อมูลที่อ้างอิง ( ส่วนแบ่ง , แผนที่ , จัง ) และเริ่มต้นโครงสร้างข้อมูลพื้นฐานของพวกเขา

    ตัวอย่าง: ในกรณีของ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


  • สิ่งต่าง ๆ ชัดเจนมากขึ้นด้วยตัวอย่าง upvoted :)
    Sumit Jha

    8

    คุณต้องmake()สร้างช่องและแผนที่ (และชิ้นส่วน แต่สามารถสร้างได้จากอาร์เรย์ด้วย) ไม่มีทางเลือกอื่นในการทำสิ่งเหล่านี้ดังนั้นคุณจึงไม่สามารถลบออกmake()จากพจนานุกรมของคุณ

    สำหรับnew()ฉันไม่รู้เหตุผลใด ๆ เลยว่าทำไมคุณถึงต้องการเมื่อคุณสามารถใช้ไวยากรณ์ของ struct มันมีความหมายเชิงความหมายที่ไม่ซ้ำกันซึ่งก็คือ "สร้างและส่งกลับโครงสร้างที่มีเขตข้อมูลทั้งหมดเริ่มต้นให้เป็นค่าศูนย์ของพวกเขา" ซึ่งจะมีประโยชน์


    1
    ใหม่ดังนั้นควรหลีกเลี่ยงและต้องการใช้ไวยากรณ์ของโครงสร้าง
    CommonSenseCode

    8

    นอกเหนือจากทุกอย่างที่อธิบายไว้ในEffective Goความแตกต่างที่สำคัญระหว่างnew(T)และ&T{}คือการจัดสรรฮีปอย่างชัดเจน อย่างไรก็ตามควรสังเกตว่านี่ขึ้นอยู่กับการใช้งานและอาจมีการเปลี่ยนแปลงได้

    เมื่อเปรียบเทียบmakeกับnewความรู้สึกเล็กน้อยขณะที่ทั้งสองทำหน้าที่ต่างกันโดยสิ้นเชิง แต่นี่จะอธิบายรายละเอียดในบทความที่เชื่อมโยง


    10
    การอ้างสิทธิ์ที่&T{}ดำเนินการจัดสรรฮีปอย่างชัดเจนคือ AFAIK ไม่ได้ยึดตามข้อกำหนดใด ๆ อันที่จริงผมเชื่อว่าการวิเคราะห์การหลบหนีอยู่แล้วการรักษา T * new(T)เช่นในกองเมื่อใดก็ตามที่เป็นไปได้ในทางเดียวกันว่าเช่นเดียวกับ
    zzzz

    6

    ใหม่ (T): มันจะส่งคืนตัวชี้เพื่อพิมพ์ค่า T ประเภท * T มันจัดสรรและศูนย์หน่วยความจำ ใหม่ (T) เทียบเท่ากับ & T {}

    ทำให้ (T): มันส่งกลับค่าเริ่มต้นของประเภท Tมันจะจัดสรรและเริ่มต้นหน่วยความจำ มันใช้สำหรับชิ้นแผนที่และช่อง

    โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
    Licensed under cc by-sa 3.0 with attribution required.