Class và Struct là những thứ bất cứ 1 lập trình viên cũng thường xuyên sử dụng. Tuy nhiên, đối với những người mới thì việc phân biệt giữa class và struct là vẫn còn mơ hồ.
Ở bài viết này, mình sẽ nói về các điểm khác nhau giữa Class và Struct.
Nội dung bài viết:
- Struct và Class là gì?
- Điểm giống nhau giữa Struct và Class
- Điểm khác nhau giữa Struct và Class
- Khi nào nên sử dụng struct / class
Struct và Class là gì?
Struct và class là các cấu trúc linh hoạt, được sử dụng với nhiều mục đích khác nhau để trở thành các khối xây dựng chương trình của ban. Bạn định nghĩa các thuộc tính và phương thức để thêm vào các struct/class của bạn.
Điểm giống nhau giữa struct và class:
Struct và Class đều có thể:
- Định nghĩa, khai báo các thuộc tính và hàm.
- Khai báo subscripts.
class Rank {
subscript (index: Int) -> String {
switch index {
case 1: return "First"
case 2: return "Second"
case 3: return "Three"
default: return "Dont have rank"
}
}
}
let rank = Rank()
print (rank[1]) // -> "First"
print (rank[12]) // -> "Dont have rank"
- Khai báo các initializers để khởi tạo.
- Có thể mở rộng bằng extension.
- Có thể implement các protocol để cung cấp các chức năng tiêu chuẩn.
Điểm khác nhau giữa Struct và Class:
Initialize:
Khi định nghĩa 1 class, bạn bắt buộc phải khởi tạo 1 hàm init cho các thuộc tính không phải optional hoặc chưa có giá trị default.
class Car {
let id: Int = 1
var color: UIColor?
var price: Double
init(price: Double) {
self.price = price
}
}
let car1 = Car(price: 5000)
Còn khi định nghĩa 1 struct, bạn không cần phải khởi tạo 1 hàm init bởi khi đó Struct đã tự định nghĩa 1 hàm init default cho bạn.
struct Car {
let id: Int = 1
var color: UIColor
var price: Double
}
let car1 = Car(color: .red, price: 5000)
Tuy nhiên, nếu bạn khai báo thêm các init khác thì hàm init default của struct sẽ bị mất.
Vì vậy, để tránh điều này, chỉ cần khai báo các hàm init mới ở extension thì hàm init default sẽ không bị mất.
Struct là Value types còn Class là Reference types
Value type: 1 instance có kiểu là value type thì nó sẽ tự tạo ra các bản copy các giá trị của mình để truyền đi mỗi khi được nó đươc dùng để gán cho các instance khác, hoặc khi được dùng để truyền vào hàm. Bởi vậy, nếu bạn thay đổi giá trị các bản copy thì giá trị bản gốc cũng sẽ không bị thay đôi:
let car1 = Car(price: 5000)
var car2 = car1
car2.price = 10000
print(car1.price) // -> 5000.0
print(car2.price) // -> 10000.0
Reference type: Thay vì việc tạo ra các bản sao, thì 1 instance kiểu reference type sẽ tự truyền đi 1 tham chiếu tới chính nó khi được gán cho các insstance khác hoặc khi được truyền vào hàm.
let car1 = Car(price: 5000)
let car2 = car1
car2.price = 10000
print(car1.price) // -> 10000.0
print(car2.price) // -> 10000.0
print(car1 === car2) // -> true
Có thể hiểu đơn giản rằng, car1 sẽ tự gán chính bản thân nó cho car2 chứ không tạo ra các bản copy như struct, bởi vậy khi thay đổi thuộc tính của car2 thì car1 cũng bị thay đổi theo.
Note: Có thể kiểm tra 2 đối tượng có cùng trỏ tới 1 instance hay không bằng toán từ ===
Class có thể kế thừa, còn struct thì không
Class hỗ trợ kế thừa, có thể tạo ra các class con kế thừa từ class cha để mang những thuộc tính, phương thức của class cha. Có thể thấy class hỗ trợ lập trình OOP tốt hơn struct.
Các phương thức trong struct nếu muốn thay đổi thuọc tính thì phải thêm mutating
Struct là kiểu value type. Mặc định thì các thuộc tính của 1 biến kiểu value type không thể bị sửa đổi trong các hàm của biến đó.
Tuy nhiên, nếu bạn muốn thay đổi thuộc tính của 1 Struct bằng 1 phương thức bên trong nó, bạn phải khai báo mutating vào trước phương thức để làm:
Class hỗ trợ hàm deinit:
Class cung cấp hàm deinit. Hàm này được gọi trước khi 1 class được giải phóng khỏi memory.
Ví dụ về sự khác biệt struct và class thường dùng
Ví dụ như trong ví dụ sau đây đối với hàm repeating rất hay được sử dụng:
class Dog {
var name = ""
}
let dogs = [Dog](repeating: Dog(), count: 3)
dogs[0].name = "Green"
dogs[1].name = "Red"
dogs[2].name = "Blue"
print("\(dogs[0].name) \(dogs[1].name) \(dogs[2].name)") // Blue Blue Blue
Bởi Dog là 1 class, nên 3 đối tượng Dog trong mảng dogs sẽ cùng trỏ tới 1 đối tượng giống nhau -> Vì vậy nên thay đổi giá trị của dogs[3] cũng sẽ thay đổi giá trị của các dog còn lại trong array.
Thử sửa Dog thành kiểu struct và print ra kết quả.
Khi nào nên sử dụng struct / class
Recommend sử dụng struct bởi:
- Struct nhanh hơn class bởi struct sử dụng method dispatch là static dispatch, class sử dụng dynamic dispatch. Ngoài ra, struct lưu dữ liệu trong stack, còn class sử dụng stack + heap -> Xử lí trong class sẽ lâu hơn.
- Class là 1 reference type. Do đó, nếu không cẩn thận khi truyền biến sẽ dễ gây ra lỗi ngoài ý muốn ( Xem phần value type vs reference type ở trên). -> Sử dụng struct sẽ an toàn hơn.
Nên sử dụng class khi:
- Cần sử dụng kế thừa.
- Cần sử dụng reference type
Kết luận:
Việc phân biệt được sự khác nhau giữa class và struct vô cùng quan trọng để có thể sử dụng cho đúng cách. Hi vọng qua bài viết, các bạn sẽ hiểu rõ hơn được về sự khác biệt giữa class và struct.