Чем отличаются структуры и классы
В языках программирования, таких как Swift, C++, C#, а также частично в Go, различие между структурами (struct) и классами (class) заключается не только в синтаксисе, но и в поведении, семантике копирования, управлении памятью, наследовании и принципах передачи по значению или по ссылке.
Ниже представлено подробное сравнение между структурами и классами на примере Swift и других языков, где применимы схожие принципы.
📦 Основные различия: значение против ссылки
Характеристика | Структура (struct) | Класс (class) |
---|---|---|
Тип передачи | По значению | По ссылке |
--- | --- | --- |
Наследование | ❌ Не поддерживается | ✅ Поддерживается |
--- | --- | --- |
Управление памятью | Автоматически (stack/COW) | Через reference counting (ARC) |
--- | --- | --- |
Может быть изменён в методе | Только с mutating | Методы могут свободно изменять |
--- | --- | --- |
Поддержка ARC | ❌ | ✅ |
--- | --- | --- |
Identity (сравнение ===) | ❌ Нет идентичности | ✅ Имеет идентичность |
--- | --- | --- |
Поддержка деинициализации | ❌ Нет deinit | ✅ Есть deinit |
--- | --- | --- |
📌 Передача по значению и по ссылке
Структуры: копируются
struct Point {
var x: Int
var y: Int
}
var p1 = Point(x: 1, y: 2)
var p2 = p1 // копия p1
p2.x = 10
// p1.x всё ещё равен 1
Классы: передаются по ссылке
class Point {
var x: Int
var y: Int
init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
var p1 = Point(x: 1, y: 2)
var p2 = p1 // p2 указывает на тот же объект
p2.x = 10
// p1.x тоже станет 10, потому что они ссылаются на один объект
🧠 Поведение в памяти
Структуры
-
Обычно размещаются в стеке, но при использовании COW (Copy-on-Write) могут быть размещены и в куче.
-
Уничтожаются автоматически по завершению области видимости.
-
Нет необходимости в управлении памятью вручную или через ARC.
Классы
-
Объекты всегда размещаются в куче.
-
Управление памятью осуществляется через ARC (Automatic Reference Counting) в Swift или GC (Garbage Collector) в C# и Java.
-
Использование ссылок требует отслеживания retain count, а значит — риск retain cycle.
🧬 Наследование и полиморфизм
Структуры
-
Не поддерживают наследование.
-
Можно использовать протоколы (интерфейсы) для достижения полиморфизма.
protocol Drawable {
func draw()
}
struct Circle: Drawable {
func draw() { print("Draw circle") }
}
Классы
-
Поддерживают полное наследование: суперклассы, переопределение методов, доступ к super.
-
Позволяют использовать динамическое связывание (dynamic dispatch).
class Shape {
func draw() { print("Shape") }
}
class Circle: Shape {
override func draw() { print("Circle") }
}
⚙️ Изменяемость (mutability)
Структуры
-
Immutable по умолчанию, если переменная объявлена как let, даже внутренние свойства не изменяются.
-
Методы, изменяющие свойства, должны быть помечены как mutating.
struct Counter {
var count = 0
mutating func increment() {
count += 1
}
}
Классы
- Экземпляры изменяемы, даже если они объявлены через let, если свойства объявлены как var.
class Counter {
var count = 0
func increment() {
count += 1
}
}
🔁 Identity vs Equality
-
Структуры не имеют идентичности — сравниваются по значению.
-
Классы обладают идентичностью — можно проверить, указывает ли ссылка на тот же объект через оператор ===.
let a = SomeClass()
let b = a
print(a === b) // true
🧹 Жизненный цикл: init и deinit
Структуры
-
Могут иметь init, в том числе с дефолтными значениями.
-
Не поддерживают deinit.
Классы
- Поддерживают деструкторы (deinit), позволяя выполнять действия при освобождении памяти (например, закрытие соединений, удаление наблюдателей и т.п.).
🧰 Применение: когда что использовать
Когда использовать структуры:
-
Данные не должны иметь идентичность.
-
Копирование безопасно и желательно.
-
Структура не будет изменяться многими объектами одновременно.
-
Лёгкие объекты, не обременённые ссылками (например, координаты, векторы, настройки и т.д.).
-
Лучше работает в многопоточном окружении за счёт изоляции данных.
Примеры: CGPoint, CGSize, CGRect, Date, Range, URL.
Когда использовать классы:
-
Объект имеет идентичность, жизненный цикл должен отслеживаться.
-
Объект будет передаваться между многими компонентами по ссылке.
-
Требуется наследование.
-
Нужна слабая ссылка (weak) — возможно только с классами.
-
Требуется деструктор deinit.
Примеры: UIView, UIViewController, URLSession, NSOperation.
🌐 В других языках
C++
-
struct и class почти идентичны по возможностям.
-
Основное отличие — struct имеет public члены по умолчанию, class — private.
struct Point {
int x, y;
}; // x и y — public по умолчанию
class Point {
int x, y; // private по умолчанию
};
C#
-
Имеется чёткое разделение:
-
struct — value type, размещается в стеке;
-
class — reference type, размещается в куче.
-
-
struct в C# не поддерживает наследование (кроме интерфейсов).
-
C# struct не может иметь деструкторы и инициализаторы по умолчанию.
Go
-
В Go есть только struct, но всё передаётся по значению по умолчанию.
-
Наследование нет, только композиция.
-
Методы можно определять как для значения, так и для указателя (*Type), что приближает поведение к классам.
💡 Расширенные возможности в Swift
-
Struct + Protocols + Generics → альтернатива классической ООП-модели.
-
Copy-on-Write: многие структуры (например, Array, Dictionary, Set, String) используют COW, чтобы совмещать эффективность с безопасностью копирования.
📎 Итоги различий
Категория | Struct | Class |
---|---|---|
Тип | Value Type | Reference Type |
--- | --- | --- |
Место хранения | Stack (или Heap при COW) | Heap |
--- | --- | --- |
Передача | Копирование | Ссылка |
--- | --- | --- |
Наследование | Нет | Да |
--- | --- | --- |
ARC | Не участвует | Участвует |
--- | --- | --- |
Mutating | Требует mutating | Нет требований |
--- | --- | --- |
deinit | Нет | Да |
--- | --- | --- |
Сравнение | По значению | По ссылке |
--- | --- | --- |
Поддержка weak/unowned | Нет | Да |
--- | --- | --- |