Hàm new()
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 22 trên 107.
Go cung cấp một hàm tích hợp có tên new(), hàm này cấp phát bộ nhớ cho một kiểu dữ liệu và trả về con trỏ đến kiểu đó. Bộ nhớ được cấp phát sẽ được khởi tạo bằng giá trị không của kiểu dữ liệu đó.
p := new(int) // Cấp phát bộ nhớ cho một số nguyên (integer)
fmt.Println(*p) // 0 (giá trị không)
fmt.Println(p) // 0xc000018030 (địa chỉ bộ nhớ)Hàm (function) new() nhận một kiểu dữ liệu làm đối số và trả về con trỏ đến một giá trị zero mới được cấp phát thuộc kiểu đó. Điều này đặc biệt hữu ích khi bạn cần một con trỏ nhưng không có biến nào sẵn để tham chiếu.
Với struct, new() tạo một con trỏ đến struct với tất cả trường được đặt thành giá trị không.
type Person struct {
Name string
Age int
}
p := new(Person)
fmt.Println(p.Name) // "" (chuỗi rỗng)
fmt.Println(p.Age) // 0Có thể bạn đang băn khoăn khi nào nên dùng new() thay vì toán tử địa chỉ &. Hai cách dưới đây tương đương nhau:
// Dùng new()
p1 := new(Person)
// Dùng toán tử lấy địa chỉ với giá trị trực tiếp
p2 := &Person{}Cú pháp (syntax) &Person{} phổ biến hơn trong thực tế vì cho phép bạn khởi tạo các trường cùng lúc. Dùng new() khi bạn muốn tạo một instance có giá trị bằng không và ưu tiên ngữ nghĩa cấp phát bộ nhớ tường minh.
Thử thách
DễHãy xây dựng một trình quản lý cấu hình để minh họa cách new() cấp phát bộ nhớ và khởi tạo các struct về giá trị 0. Bạn sẽ thấy cách làm này khác với việc dùng toán tử lấy địa chỉ cùng một struct literal như thế nào.
Bạn sẽ sắp xếp code trong hai tệp:
config.go: Định nghĩa một structServerConfigvới các trườngHost(string),Port(int),MaxConnections(int) vàDebugMode(bool). Tạo hai hàm:NewServerConfigdùngnew()để cấp phát mộtServerConfigrồi trả về trực tiếp con trỏ (với tất cả giá trị 0)ConfigureServernhận một con trỏ*ServerConfigcùng với chuỗi host và số nguyên port, sau đó dùng con trỏ để đặt các trường đó trong cấu hình
Displaytrên*ServerConfigđể trả về một chuỗi đã định dạng, hiển thị tất cả giá trị cấu hình.main.go: Đọc host và port từ đầu vào. Trước tiên, tạo một cấu hình bằngNewServerConfig()rồi hiển thị cấu hình để cho thấy các giá trị 0. Sau đó dùngConfigureServerđể đặt host và port, rồi hiển thị lại cấu hình để cho thấy các giá trị đã cập nhật.
Đầu vào sẽ có dạng như sau:
- Dòng 1: Tên host (string)
- Dòng 2: Số port (số nguyên)
Phương thức Display của bạn cần trả về một chuỗi theo đúng định dạng này:
Host: [Host]
Port: [Port]
MaxConnections: [MaxConnections]
DebugMode: [DebugMode]In kết quả hiển thị hai lần, một lần trước khi cấu hình (cho thấy các giá trị 0) và một lần sau khi cấu hình (cho thấy host và port đã cập nhật).
Ví dụ, với localhost và 8080, kết quả của bạn sẽ là:
Host:
Port: 0
MaxConnections: 0
DebugMode: false
Host: localhost
Port: 8080
MaxConnections: 0
DebugMode: falseLưu ý rằng lần hiển thị đầu tiên cho thấy tất cả giá trị 0 (chuỗi rỗng, 0, 0, false) vì new() khởi tạo mọi thứ về giá trị 0. Sau khi gọi ConfigureServer, chỉ host và port thay đổi, còn các trường khác vẫn giữ giá trị 0.
Tự làm thử
package main
import (
"fmt"
)
func main() {
// Đọc đầu vào
var host string
var port int
fmt.Scanln(&host)
fmt.Scanln(&port)
// TODO: Tạo cấu hình bằng NewServerConfig() và hiển thị cấu hình đó
// Thao tác này sẽ hiển thị các giá trị mặc định
// TODO: Dùng ConfigureServer để đặt host và port
// TODO: Hiển thị lại cấu hình để xem các giá trị đã cập nhật
}
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
1Nền tảng OOP trong Go
Tệp bên ngoàiWorkspace và module trong GoPackage và importTên được export và không được exportGiới thiệu về OOP trong GoStruct như lớpĐịnh nghĩa phương thức cho structReceiver kiểu con trỏ và kiểu giá trịKhởi tạo structHàm khởi tạoÔn tập: máy tính đơn giản4Interface
Giới thiệu về interfaceTriển khai ngầm địnhInterface như một hợp đồngInterface rỗng (any)Khẳng định kiểuswitch kiểuKết hợp interfaceInterface Stringer và ErrorÔn tập: Máy tính hình học7Tính đóng gói
Trường được xuất và không được xuấtĐóng gói ở cấp packagePhương thức getter và setterẨn thông tin trong GoÔn tập: hồ sơ học sinh10Generics (Go 1.18 trở lên)
Giới thiệu về GenericsTham số kiểuRàng buộc kiểuStruct tổng quátGiải pháp thay thế cho phương thức tổng quátÔn tập: tập hợp tổng quát13Các mẫu thiết kế phần 1
Giới thiệu về mẫu thiết kếMẫu SingletonMẫu FactoryMẫu Abstract FactoryMẫu ObserverMẫu Strategy2Tìm hiểu chuyên sâu về kiểu dữ liệu và struct
Kiểu cơ bản và kiểu tổng hợpĐịnh nghĩa kiểu dữ liệu tùy chỉnhThẻ structStruct ẩn danhStruct lồng nhauGiá trị zero và giá trị mặc địnhÔn tập: sổ liên lạc5Ưu tiên kết hợp hơn kế thừa
Vì sao Go không có kế thừaNhập môn nhúng structTự động nâng phương thứcNhúng nhiều structNhúng và kết tậpChe khuất phương thức được nhúngÔn tập: hệ phân cấp nhân viên8Xử lý lỗi và lập trình hướng đối tượng
Giao diện errorCác kiểu lỗi tùy chỉnhBọc lỗi với fmt.ErrorfLỗi sentinelerrors.Is() và errors.As()Panic, defer và recoverÔn tập: trình phân tích tệp3Con trỏ và bộ nhớ
Cơ bản về con trỏ trong GoCon trỏ đến structTruyền theo giá trị và tham chiếuHàm new()Thu gom rác trong GoÔn tập: xây dựng danh sách liên kết6Đa hình trong Go
Đa hình qua interfaceĐịnh kiểu vịt trong GoQuy tắc thỏa mãn interfaceCác tập hợp đa hìnhTiêm phụ thuộcÔn tập: bộ xử lý thanh toán9Lập trình đồng thời và lập trình hướng đối tượng
Cơ bản về goroutineChannel và giao tiếpChannel có bộ đệm và không có bộ đệmCâu lệnh selectsync.Mutex và sync.RWMutexsync.WaitGroupThiết kế struct an toàn luồngÔn tập: worker poolTự luyện tập: Trình biên dịch Go online