Məzmuna keç
Educora
Orta16 dəq6 / 10

Strukturlar, metodlar və göstəricilər

Öz tiplərini strukturlarla qur, göstəricilərin nə üçün lazım olduğunu anla, qiymət və göstərici qəbuledicili metodlar yaz, kompozisiyadan istifadə et.

Özünü yoxla
Bu dərsdə öyrənəcəksən
  • Struktur elan etmək, literal ilə yaratmaq və %+v ilə çap etmək
  • & və * ilə göstəricilərdən istifadə etmək
  • Qiymət və göstərici qəbuledicisi arasında düzgün seçim etmək
  • NewX konstruktor funksiyaları və strukturların daxil edilməsi ilə işləmək

Şagird haqqında məlumat — ad, yaş, orta bal — bir yerdə saxlanılmalıdır. Onları üç ayrı dəyişəndə saxlamaq əlverişsizdir. Go-da sinif yoxdur, əvəzində strukturlar (struct) var: onlar əlaqəli sahələri bir tipdə birləşdirir, metodlar isə bu tipə davranış əlavə edir. Bu dərsdə həm də göstəriciləri öyrənəcəksən — onlarsız funksiya orijinal məlumatı dəyişə bilməz.

Strukturlar

Struktur type Ad struct { ... } şəklində elan olunur, içində isə sahələr və onların tipləri sadalanır. Obyekt yaratmaq üçün struktur literalı yazılır: Student{Name: "Aysel", Age: 15}. Sahələrə nöqtə ilə müraciət olunur: s.Name. Qiymət verilməyən sahə sıfır qiymət alır, ona görə boş struktur da tam işlək qiymətdir.

Go
package main

import "fmt"

type Student struct {
	Name  string
	Age   int
	Score float64
}

func main() {
	s := Student{Name: "Aysel", Age: 15, Score: 92.5}
	var empty Student
	s.Age++
	fmt.Println(s)
	fmt.Printf("%+v\n", s)
	fmt.Printf("%+v\n", empty)
	fmt.Println(s.Name, s.Score)
}
Gözlənilən nəticə
{Aysel 16 92.5}
{Name:Aysel Age:16 Score:92.5}
{Name: Age:0 Score:0}
Aysel 92.5

%v (və Println) yalnız qiymətləri göstərir, %+v isə sahələrin adlarını da əlavə edir — sazlama (debug) zamanı bu çox faydalıdır. Bütün sahələri müqayisə oluna bilən strukturları == ilə müqayisə etmək olar: iki Student bütün sahələri eyni olduqda bərabərdir.

Göstəricilər

Go-da funksiyaya ötürülən hər şey surətlənir. Funksiya parametri dəyişsə, çağıran tərəfin dəyişəni toxunulmaz qalır. Orijinalı dəyişmək üçün göstərici lazımdır — dəyişənin yaddaşdakı ünvanı. &x ünvanı götürür, *p isə həmin ünvandakı qiyməti oxuyur və ya dəyişir. *int tipi «int-ə göstərici» deməkdir. Go-da göstərici hesabı (C-dəki kimi p++) yoxdur, ona görə göstəricilər təhlükəsizdir.

Go
package main

import "fmt"

func doubleValue(n int) {
	n *= 2
}

func doublePointer(n *int) {
	*n *= 2
}

func main() {
	x := 5
	p := &x
	*p = 7
	fmt.Println(x, *p, p == &x)
	doubleValue(x)
	fmt.Println(x)
	doublePointer(&x)
	fmt.Println(x)
}
Gözlənilən nəticə
7 7 true
7
14
Tərif
Göstərici (pointer)

Başqa dəyişənin yaddaşdakı ünvanını saxlayan qiymət. Sıfır qiyməti nil-dir. & ünvanı götürür, * isə həmin ünvandakı qiymətə müraciət edir.

Metodlar: qiymət və göstərici qəbulediciləri

Metod — xüsusi parametri, yəni qəbuledicisi (receiver) olan funksiyadır. Qəbuledici func sözü ilə metodun adı arasında yazılır: func (a Account) Summary() string. Əgər qəbuledici qiymətdirsə (a Account), metod obyektin surəti ilə işləyir. Əgər göstəricidirsə (a *Account), metod orijinal obyekti dəyişə bilir.

Go
package main

import "fmt"

type Account struct {
	Owner   string
	Balance float64
}

func (a Account) Summary() string {
	return fmt.Sprintf("%s: %.2f AZN", a.Owner, a.Balance)
}

func (a *Account) Deposit(amount float64) {
	a.Balance += amount
}

func (a Account) BrokenDeposit(amount float64) {
	a.Balance += amount
}

func main() {
	acc := Account{Owner: "Murad", Balance: 100}
	acc.Deposit(50)
	acc.BrokenDeposit(1000)
	fmt.Println(acc.Summary())
	p := &acc
	p.Deposit(25.5)
	fmt.Println(p.Summary())
}
Gözlənilən nəticə
Murad: 150.00 AZN
Murad: 175.50 AZN

BrokenDeposit 1000 manat əlavə etməyə çalışdı, amma balans dəyişmədi, çünki metod surət üzərində işləyirdi. Diqqət et: acc göstərici deyil, amma acc.Deposit(50) işləyir — Go avtomatik olaraq (&acc).Deposit(50) çağırır. Eyni şəkildə göstərici p üzərindən qiymət qəbuledicili Summary metodunu da çağırmaq olar.

QəbulediciNə vaxt seçməli
func (a Account)Metod obyekti dəyişmir və tip kiçikdir (bir neçə sahə, time.Time kimi).
func (a *Account)Metod sahələri dəyişir, tip böyükdür və ya içində sync.Mutex kimi surətlənməməli sahə var.
Səhv: dəyişiklik itir
type Counter struct {
	value int
}

func (c Counter) Increment() {
	c.value++ // changes a copy
}
Düzgün: göstərici qəbuledicisi
type Counter struct {
	value int
}

func (c *Counter) Increment() {
	c.value++ // changes the original
}

Konstruktorlar və daxiletmə

Go-da xüsusi konstruktor yoxdur. Əvəzində adı New ilə başlayan adi funksiya yazılır: NewTeacher(...) yeni obyekt yaradıb ona göstərici qaytarır. Varislik də yoxdur — Go kompozisiyaya üstünlük verir. Bir strukturu başqasının içinə adsız sahə kimi daxil etmək (embedding) olar: onda daxil edilmiş strukturun sahələri və metodları xarici strukturda birbaşa əlçatan olur.

Go
package main

import "fmt"

type Person struct {
	Name string
}

func (p Person) Greet() string {
	return "Hi, I am " + p.Name
}

type Teacher struct {
	Person
	Subject string
}

func NewTeacher(name, subject string) *Teacher {
	return &Teacher{Person: Person{Name: name}, Subject: subject}
}

func main() {
	t := NewTeacher("Leyla", "Math")
	fmt.Println(t.Greet())
	fmt.Println(t.Name, "teaches", t.Subject)
	fmt.Printf("%+v\n", *t)
}
Gözlənilən nəticə
Hi, I am Leyla
Leyla teaches Math
{Person:{Name:Leyla} Subject:Math}

Əsas fikirlər

  • Struktur əlaqəli sahələri bir tipdə birləşdirir; %+v onu sahə adları ilə çap edir.
  • Go hər şeyi qiymətlə ötürür; orijinalı dəyişmək üçün göstərici (&x, *p) lazımdır.
  • Qiymət qəbuledicisi surətlə, göstərici qəbuledicisi orijinalla işləyir; Go &-i özü əlavə edir.
  • nil göstərici üzərindən sahəyə müraciət panic yaradır.
  • Konstruktor əvəzinə NewX funksiyaları, varislik əvəzinə strukturların daxil edilməsi (kompozisiya) işlədilir.

Özünü yoxla

10 sual. Hər düzgün cavab XP qazandırır.

1 / 10
fmt.Printf("%+v", Point{X: 1, Y: 2}) nə çıxarır?