Değer ile Geçme ve Referans ile Geçme
Coddy'nin GO Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 21 / 107.
Go, değer ile aktarılan (pass-by-value) bir dildir. Bir değişkeni bir fonksiyona aktardığınızda, Go bu değerin bir kopyasını oluşturur. Bu, fonksiyon içindeki değişikliklerin orijinal değişkeni etkilemeyeceği anlamına gelir.
func double(n int) {
n = n * 2
}
func main() {
x := 10
double(x)
fmt.Println(x) // 10 (değişmedi)
}Aynısı struct'lar için de geçerlidir. Bir struct'ı bir fonksiyona geçirmek tüm alanlarını kopyalar, bu nedenle fonksiyon içindeki değişiklikler kaybolur.
type Person struct {
Name string
Age int
}
func birthday(p Person) {
p.Age++
}
func main() {
alice := Person{Name: "Alice", Age: 30}
birthday(alice)
fmt.Println(alice.Age) // 30 (değişmedi)
}Orijinal değeri değiştirmek için bunun yerine bir işaretçi (pointer) geçirin. İşaretçinin kendisi kopyalanır, ancak hala aynı bellek adresini gösterir; bu da orijinal veriyi değiştirmenize olanak tanır.
func birthday(p *Person) {
p.Age++
}
func main() {
alice := Person{Name: "Alice", Age: 30}
birthday(&alice)
fmt.Println(alice.Age) // 31 (değiştirildi!)
}Orijinal değeri değiştirmeniz gerektiğinde veya maliyetli kopyalamadan kaçınmak için büyük struct'ları geçirirken işaretçileri kullanın. Orijinal veriyi istenmeyen değişikliklerden korumak istediğinizde ise değerleri kullanın.
Görev
KolayStruct'ları değer (value) ile geçirmek ile işaretçi (pointer) ile geçirmek arasındaki farkı gösteren bir alışveriş sepeti sistemi kuralım. Yalnızca işaretçi parametrelerinin orijinal veriyi nasıl değiştirebildiğini ilk elden göreceksiniz.
Kodunuzu iki dosya halinde düzenleyeceksiniz:
cart.go:Name(string),Price(float64) veQuantity(int) alanlarına sahip birCartItemstruct'ı tanımlayın. Miktarı güncellenmeye çalışan iki fonksiyon oluşturun:UpdateQuantityByValuebirCartItem(değer ile) ve yeni bir miktar alır, ardından öğenin miktarını yeni değere ayarlar.UpdateQuantityByPointerbir*CartItem(işaretçi) ve yeni bir miktar alır, ardından öğenin miktarını yeni değere ayarlar.
CartItemüzerinde, fiyat ile miktarın çarpımını döndüren birTotalmetodu oluşturun.main.go: Girdiden öğe detaylarını okuyun, bir CartItem oluşturun, ardından her iki güncelleme yaklaşımını da gösterin. Önce değer fonksiyonu ile güncellemeyi deneyin ve sonucu gösterin, ardından işaretçi fonksiyonu ile güncelleyin ve sonucu tekrar gösterin.
Aşağıdaki girdiler sağlanacaktır:
- Satır 1: Öğe adı
- Satır 2: Fiyat (float)
- Satır 3: Başlangıç miktarı (tam sayı)
- Satır 4: Ayarlanacak yeni miktar (tam sayı)
Çıktı formatı:
Initial: [Name] x[Quantity] = $[Total]
After value update: [Name] x[Quantity] = $[Total]
After pointer update: [Name] x[Quantity] = $[Total]Örneğin, Laptop, 999.99, 1 ve 3 verildiğinde çıktınız şöyle olmalıdır:
Initial: Laptop x1 = $999.99
After value update: Laptop x1 = $999.99
After pointer update: Laptop x3 = $2999.97Değer güncellemesinden sonra miktarın nasıl 1'de kaldığına (fonksiyon bir kopyayı değiştirdi), ancak işaretçi güncellemesinden sonra nasıl 3'e değiştiğine (fonksiyon orijinali değiştirdi) dikkat edin. Toplamları iki ondalık basamakla formatlayın.
Kopya kağıdı
Go, değer ile geçilen (pass-by-value) bir dildir. Bir değişkeni bir fonksiyona geçirdiğinizde, Go o değerin bir kopyasını oluşturur, bu nedenle fonksiyon içindeki değişiklikler orijinal değişkeni etkilemez.
func double(n int) {
n = n * 2
}
func main() {
x := 10
double(x)
fmt.Println(x) // 10 (değişmedi)
}Aynısı struct'lar için de geçerlidir. Bir struct'ı bir fonksiyona geçirmek, tüm alanlarını kopyalar:
type Person struct {
Name string
Age int
}
func birthday(p Person) {
p.Age++
}
func main() {
alice := Person{Name: "Alice", Age: 30}
birthday(alice)
fmt.Println(alice.Age) // 30 (değişmedi)
}Orijinal değeri değiştirmek için bir pointer (işaretçi) geçirin. Pointer'ın kendisi kopyalanır, ancak hala aynı bellek adresini işaret eder:
func birthday(p *Person) {
p.Age++
}
func main() {
alice := Person{Name: "Alice", Age: 30}
birthday(&alice)
fmt.Println(alice.Age) // 31 (değiştirildi!)
}Orijinal değeri değiştirmeniz gerektiğinde veya maliyetli kopyalamadan kaçınmak için büyük struct'lar geçirirken pointer'ları kullanın. Orijinal veriyi istenmeyen değişikliklerden korumak istediğinizde ise değerleri kullanın.
Kendin dene
package main
import (
"fmt"
)
func main() {
// Girdiyi oku
var name string
var price float64
var initialQty, newQty int
fmt.Scanln(&name)
fmt.Scanln(&price)
fmt.Scanln(&initialQty)
fmt.Scanln(&newQty)
// TODO: Giriş değerleriyle bir CartItem oluşturun
// TODO: Şu formatı kullanarak başlangıç durumunu yazdırın:
// fmt.Printf("Initial: %s x%d = $%.2f\n", ...)
// TODO: UpdateQuantityByValue kullanarak miktarı güncellemeyi deneyin
// Ardından sonucu yazdırın (miktarın değişmediğine dikkat edin)
// TODO: UpdateQuantityByPointer kullanarak miktarı güncelleyin
// Ardından sonucu yazdırın (miktarın değişeceğine dikkat edin)
}
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Nesne Yönelimli Programlama bölümündeki tüm dersler
1Go OOP Temelleri
Harici DosyalarGo Çalışma Alanı ve ModüllerPaketler ve İçe AktarmalarDışa Aktarılan ve Aktarılmayan İsimlerGo'da OOP'ye GirişSınıf Olarak Struct'larStruct'larda Metot TanımlamaPointer ve Değer AlıcılarStruct BaşlatmaYapıcı FonksiyonlarÖzet - Basit Hesap Makinesi4Arayüzler
Arayüzlere GirişÖrtük UygulamaSözleşme Olarak ArayüzBoş Arayüz (any)Tür OnaylamaTür SeçimiArayüz BileşimiStringer ve Error ArayüzleriÖzet - Şekil Hesaplayıcı7Kapsülleme
Dışa Aktarılan ve Aktarılmayan AlanlarPaket Seviyesinde KapsüllemeGetter ve Setter MetotlarıGo'da Bilgi GizlemeÖzet - Öğrenci Kayıtları10Generics (Go 1.18+)
Generics'e GirişTür ParametreleriTür KısıtlamalarıGeneric Struct'larGeneric Metotlar İçin Geçici ÇözümÖzet - Generic Koleksiyon13Tasarım Kalıpları Bölüm 1
Tasarım Kalıplarına GirişSingleton KalıbıFactory KalıbıAbstract Factory KalıbıObserver KalıbıStrategy Kalıbı2Türler ve Struct'lar: Derinlemesine İnceleme
Temel ve Bileşik TürlerÖzel Tür TanımlamalarıStruct EtiketleriAnonim Struct'larİç İçe Geçmiş Struct'larSıfır Değerler ve VarsayılanlarÖzet - Rehber5Kalıtım Yerine Kompozisyon
Go'da Neden Kalıtım YokStruct Gömme TemelleriMetot YükseltmeBirden Fazla Struct GömmeGömme vs AgregasyonGömülü Metotları GölgelemeÖzet - Çalışan Hiyerarşisi8Hata Yönetimi ve OOP
error ArayüzüÖzel Hata TürleriHata Sarmalama (fmt.Errorf)Sentinel Hatalarerrors.Is() ve errors.As()Panic, Defer ve RecoverÖzet - Dosya Ayrıştırıcı3İşaretçiler ve Bellek
Go'da İşaretçi TemelleriStruct İşaretçileriDeğer ile Geçme ve Referans ile Geçmenew() FonksiyonuGo'da Garbage CollectionÖzet - Linked List Oluşturucu6Go'da Polimorfizm
Interface'ler ile PolimorfizmGo'da Duck TypingInterface Karşılama KurallarıPolimorfik KoleksiyonlarDependency InjectionÖzet - Payment Processor9Eşzamanlılık ve OOP
Goroutine TemelleriKanallar ve İletişimTamponlu ve Tamponsuz KanallarSelect İfadesisync.Mutex ve sync.RWMutexsync.WaitGroupThread-Safe Struct TasarımıÖzet - Worker Pool