ตัวรับพอยน์เตอร์ในเมธอด
เปลี่ยนแปลงสถานะของสตรักต์ผ่านตัวรับพอยน์เตอร์
ตัวรับพอยน์เตอร์ในเมธอด เป็นบทเรียน Go Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Go Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Go Academy มีบทเรียนทั้งหมด 4 บทเรียน
สรุปทบทวน: รีซีฟเวอร์แบบค่ากับรีซีฟเวอร์แบบพอยน์เตอร์
รีซีฟเวอร์แบบค่าทำงานกับสำเนาของโครงสร้าง ส่วนรีซีฟเวอร์แบบพอยน์เตอร์ทำงานกับต้นฉบับ ใช้รีซีฟเวอร์แบบพอยน์เตอร์ทุกครั้งที่เมธอดจำเป็นต้องแก้ไขสถานะ
การแก้ไขค่าด้วยรีซีฟเวอร์แบบพอยน์เตอร์
มีเพียงรีซีฟเวอร์แบบพอยน์เตอร์เท่านั้นที่สามารถแก้ไขโครงสร้างได้:
package main
import "fmt"
type Stack struct{ items []int }
func (s *Stack) Push(v int) { s.items = append(s.items, v) }
func (s *Stack) Pop() int {
n := len(s.items) - 1
v := s.items[n]
s.items = s.items[:n]
return v
}
func main() {
var st Stack
st.Push(1); st.Push(2)
fmt.Println(st.Pop()) // 2
}กฎความสอดคล้อง
หากมีเมธอดใดในชนิดข้อมูลที่ต้องใช้รีซีฟเวอร์แบบพอยน์เตอร์ เมธอดทั้งหมดก็ควรใช้รีซีฟเวอร์แบบพอยน์เตอร์ด้วย การผสมกันจะทำให้เกิดปัญหากับการรองรับอินเทอร์เฟซ:
package main
import "fmt"
type Counter struct{ n int }
func (c *Counter) Inc() { c.n++ } // pointer
func (c *Counter) Val() int { return c.n } // pointer too
func main() {
c := &Counter{}
c.Inc(); c.Inc()
fmt.Println(c.Val()) // 2
}การรองรับอินเทอร์เฟซ
เมธอดที่ใช้รีซีฟเวอร์แบบพอยน์เตอร์จะอยู่ในชุดเมธอดของ *T เท่านั้น ไม่ใช่ของ T ซึ่งส่งผลต่อการรองรับอินเทอร์เฟซ:
package main
import "fmt"
type Writer interface{ Write([]byte) }
type File struct{ name string }
func (f *File) Write(b []byte) { fmt.Println(string(b)) }
func main() {
var w Writer = &File{"out.txt"} // OK
// var w Writer = File{} — compile error
w.Write([]byte("hello"))
}การรับที่อยู่โดยอัตโนมัติ
Go จะรับที่อยู่โดยอัตโนมัติเมื่อเรียกเมธอดที่ใช้รีซีฟเวอร์แบบพอยน์เตอร์กับค่าที่สามารถระบุที่อยู่ได้:
package main
import "fmt"
type Box struct{ V int }
func (b *Box) Double() { b.V *= 2 }
func main() {
b := Box{5}
b.Double() // Go does (&b).Double()
fmt.Println(b.V) // 10
}ค่าที่ระบุที่อยู่ไม่ได้
คุณไม่สามารถเรียกเมธอดที่ใช้รีซีฟเวอร์แบบพอยน์เตอร์กับค่าที่ระบุที่อยู่ไม่ได้ เช่น สมาชิกของแมปหรือค่าที่คืนจากฟังก์ชันซึ่งไม่ได้กำหนดให้กับตัวแปร:
package main
type T struct{ V int }
func (t *T) Set(v int) { t.V = v }
func getT() T { return T{} }
func main() {
var t T
t.Set(1) // OK — t is addressable
// getT().Set(1) — ERROR: cannot take address of getT()
_ = t
}รีซีฟเวอร์ของโครงสร้างขนาดใหญ่
ใช้รีซีฟเวอร์แบบพอยน์เตอร์กับโครงสร้างขนาดใหญ่เสมอ เพื่อหลีกเลี่ยงการคัดลอกที่ใช้ทรัพยากรมากในทุกครั้งที่เรียกเมธอด:
package main
type Image struct{ pixels [1024][1024]uint8 }
// Value receiver copies 1MB per call!
func (img Image) Bad() uint8 { return img.pixels[0][0] }
// Pointer receiver — zero copy
func (img *Image) Good() uint8 { return img.pixels[0][0] }
func main() { _ = Image{} }รีซีฟเวอร์แบบพอยน์เตอร์ที่เป็น nil
เมธอดที่ใช้รีซีฟเวอร์แบบพอยน์เตอร์สามารถจัดการกรณี nil ได้อย่างเหมาะสม:
package main
import "fmt"
type List struct{ head *node }
type node struct{ val int; next *node }
func (l *List) Len() int {
if l == nil { return 0 }
count := 0
for n := l.head; n != nil; n = n.next { count++ }
return count
}
func main() {
var l *List
fmt.Println(l.Len()) // 0
}การฝังชนิดข้อมูลและรีซีฟเวอร์แบบพอยน์เตอร์
เมื่อฝังชนิดข้อมูลที่มีรีซีฟเวอร์แบบพอยน์เตอร์ โครงสร้างภายนอกก็ต้องใช้งานในรูปพอยน์เตอร์ด้วย จึงจะเข้าถึงเมธอดที่เลื่อนระดับขึ้นมาได้:
package main
import "fmt"
type Base struct{ V int }
func (b *Base) Set(v int) { b.V = v }
type Derived struct{ Base }
func main() {
d := &Derived{}
d.Set(42) // promoted pointer-receiver method
fmt.Println(d.V) // 42
}การคัดลอกมิวเทกซ์
อย่าคัดลอกโครงสร้างที่มี sync.Mutex — ใช้รีซีฟเวอร์แบบพอยน์เตอร์หรือส่งผ่านด้วยพอยน์เตอร์เสมอ การคัดลอกมิวเทกซ์ที่ถูกล็อกจะทำให้เกิดการหยุดรอไม่สิ้นสุด:
package main
import "sync"
type Safe struct {
mu sync.Mutex
v int
}
// Correct: pointer receiver
func (s *Safe) Set(v int) {
s.mu.Lock()
defer s.mu.Unlock()
s.v = v
}
func main() { _ = Safe{} }go vet ตรวจจับข้อผิดพลาด
เรียกใช้ go vet ./... เพื่อตรวจจับข้อผิดพลาดทั่วไปเกี่ยวกับรีซีฟเวอร์แบบพอยน์เตอร์ เช่น การคัดลอกมิวเทกซ์และล็อก คำสั่งนี้เป็นส่วนหนึ่งของกระบวนการสร้างด้วย go build และการตรวจสอบ CI
ตรวจสอบอย่างรวดเร็ว
เมธอดที่ใช้รีซีฟเวอร์แบบพอยน์เตอร์อยู่ในชุดเมธอดของชนิดข้อมูลใด
สรุปทบทวน
ประเด็นสำคัญ:
- รีซีฟเวอร์แบบพอยน์เตอร์แก้ไขค่าได้ ส่วนรีซีฟเวอร์แบบค่าแก้ไขไม่ได้
- ให้เมธอดทั้งหมดของชนิดข้อมูลหนึ่งใช้รีซีฟเวอร์ชนิดเดียวกัน
- มีเพียง
*Tเท่านั้นที่รองรับอินเทอร์เฟซซึ่งต้องใช้เมธอดแบบรีซีฟเวอร์พอยน์เตอร์ - อย่าคัดลอกโครงสร้างที่มีมิวเทกซ์ — ใช้รีซีฟเวอร์แบบพอยน์เตอร์
โจทย์ฝึกปฏิบัติ
สร้างโครงสร้าง Queue พร้อมเมธอดแบบรีซีฟเวอร์พอยน์เตอร์ ได้แก่ Enqueue(int), Dequeue() (int, bool) และ Len() int โดยใช้สไลซ์ []int เป็นโครงสร้างข้อมูลเบื้องหลัง
คำถามที่พบบ่อย
บทเรียน “ตัวรับพอยน์เตอร์ในเมธอด” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ตัวรับพอยน์เตอร์ในเมธอด” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Go Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Go Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ตัวรับพอยน์เตอร์ในเมธอด”
เปลี่ยนแปลงสถานะของสตรักต์ผ่านตัวรับพอยน์เตอร์ คุณปฏิบัติ Go Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Go Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Go Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “ตัวรับพอยน์เตอร์ในเมธอด” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Go Academy นี้ได้ไหม
ได้ บทเรียน Go Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- พอยน์เตอร์คืออะไร
- พอยน์เตอร์และฟังก์ชัน
- ตัวรับพอยน์เตอร์ในเมธอด
- new() และกรณีที่ควรใช้พอยน์เตอร์