Menu
CoddyTech

Con trỏ đến struct

Thuộc phần Lập trình hướng đối tượng trong hành trình GO của Coddy. Bài học 20 trên 107.

Khi làm việc với struct, bạn thường dùng con trỏ để tránh sao chép các cấu trúc dữ liệu lớn và để sửa đổi struct gốc. Con trỏ đến struct lưu địa chỉ bộ nhớ của instance struct đó.

Bạn có thể tạo con trỏ struct bằng toán tử & với một struct có sẵn hoặc dùng trực tiếp toán tử địa chỉ với một struct literal.

type Person struct {
    Name string
    Age  int
}

p := Person{Name: "Alice", Age: 30}
ptr := &p  // con trỏ trỏ tới struct đã có

// Hoặc tạo trực tiếp
ptr2 := &Person{Name: "Bob", Age: 25}

Go cung cấp cách viết tắt tiện lợi khi truy cập các trường của struct thông qua con trỏ. Thay vì viết (*ptr).Name, bạn chỉ cần viết ptr.Name. Go tự động giải tham chiếu con trỏ giúp bạn.

ptr := &Person{Name: "Alice", Age: 30}

// Cả hai cách hoạt động giống nhau
fmt.Println((*ptr).Name)  // Giải tham chiếu tường minh
fmt.Println(ptr.Name)     // Giải tham chiếu tự động

Cơ chế tự động giải tham chiếu này cũng áp dụng khi sửa đổi các trường. Những thay đổi thông qua con trỏ sẽ ảnh hưởng đến struct ban đầu.

p := Person{Name: "Alice", Age: 30}
ptr := &p

ptr.Age = 31           // thay đổi giá trị ban đầu
fmt.Println(p.Age)     // 31

Con trỏ struct rất cần thiết trong các mẫu OOP của Go. Chúng cho phép phương thức sửa đổi trạng thái của receiver, giúp truyền các struct lớn vào hàm một cách hiệu quả và tạo nền tảng để xây dựng các cấu trúc dữ liệu liên kết như cây và danh sách.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống tài khoản ngân hàng để minh họa cách con trỏ đến struct cho phép bạn sửa đổi dữ liệu gốc một cách hiệu quả. Bạn sẽ tạo một tài khoản có thể cập nhật thông qua các thao tác với con trỏ.

Bạn sẽ sắp xếp code trong hai tệp:

  • account.go: Định nghĩa một struct BankAccount với các trường Owner (chuỗi), Balance (float64) và AccountNumber (chuỗi). Tạo một hàm (function) tên là NewAccount, nhận tên chủ tài khoản và số tài khoản làm tham số, rồi trả về một con trỏ đến BankAccount mới có số dư ban đầu là 0. Tạo thêm hàm Deposit, nhận một con trỏ *BankAccount và một khoản tiền, sau đó cộng khoản tiền đó vào số dư tài khoản thông qua con trỏ.
  • main.go: Đọc thông tin tài khoản từ đầu vào, tạo tài khoản bằng hàm khởi tạo (constructor) của bạn, hàm này trả về một con trỏ, gửi tiền vào tài khoản bằng con trỏ, rồi hiển thị thông tin tài khoản để xác nhận số dư đã thay đổi.

Đầu vào sẽ gồm:

  • Dòng 1: Tên chủ tài khoản
  • Dòng 2: Số tài khoản
  • Dòng 3: Số tiền gửi (số thực)

Định dạng đầu ra:

Account: [AccountNumber]
Owner: [Owner]
Balance: [Balance]

Ví dụ, với đầu vào John Smith, ACC-12345 và 500.50, đầu ra của bạn sẽ là:

Account: ACC-12345
Owner: John Smith
Balance: 500.50

Hãy nhớ rằng hàm NewAccount cần trả về *BankAccount bằng toán tử &, còn hàm Deposit có thể sửa đổi số dư trực tiếp thông qua con trỏ bằng cách Go tự động giải tham chiếu, chẳng hạn dùng account.Balance thay cho (*account).Balance.

Tự làm thử

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Đọc tên chủ tài khoản
	ownerName, _ := reader.ReadString('\n')
	ownerName = strings.TrimSpace(ownerName)
	
	// Đọc số tài khoản
	accountNumber, _ := reader.ReadString('\n')
	accountNumber = strings.TrimSpace(accountNumber)
	
	// Đọc số tiền gửi
	amountStr, _ := reader.ReadString('\n')
	amountStr = strings.TrimSpace(amountStr)
	amount, _ := strconv.ParseFloat(amountStr, 64)
	
	// TODO: Tạo tài khoản mới bằng hàm (function) NewAccount (trả về con trỏ)
	
	// TODO: Gửi tiền bằng hàm Deposit với con trỏ tài khoản
	
	// TODO: In thông tin tài khoản theo định dạng yêu cầu:
	// Tài khoản: [AccountNumber]
	// Chủ tài khoản: [Owner]
	// Số dư: [Balance]
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Go online