Класс vs объект — как устроена объектная модель Simula и Smalltalk

Класс и объект — базовые понятия ООП. Класс представляет собой шаблон, описывающий поля и методы, а объект является его экземпляром в памяти. Термин ввели Оле-Йохан Даль и Кристен Нюгорд в языке Simula 67 в 1967 году, а полностью объектную модель предложил Алан Кей в языке Smalltalk-72.

Определение шаблона типа и его экземпляра

Класс — это описание типа: множества полей и методов, которые получит каждый объект данного типа. Согласно официальной спецификации Java Language Specification §8, декларация вводит новый ссылочный тип. Само описание не занимает память под данные конкретного экземпляра до момента создания — в оперативной памяти находится только описание типа и таблица методов.

Экземпляр (instance) — конкретная реализация шаблона, размещённая в куче и имеющая собственный адрес. Python Language Reference §3.1 определяет объект как «Python’s abstraction for data»: у каждого экземпляра есть идентификатор, тип и значение. Объект хранит собственные значения полей, но методы разделяет с другими экземплярами того же типа через таблицу виртуальных функций.

Разница фиксируется на трёх уровнях. Шаблон существует один в единице компиляции, экземпляр — многократно. Первый описывает интерфейс, второй содержит состояние. Декларация наследуется и является статической сущностью, экземпляр живёт в динамической памяти и удаляется сборщиком мусора или деструктором. По данным Oracle Technical Note от 2019 года, до 70% ошибок работы с памятью в языках без GC связаны именно с неверным жизненным циклом экземпляра.

История понятий: Simula 67 и Smalltalk-72

Первым языком с классами стал Simula 67, разработанный в Норвежском вычислительном центре в 1967 году. Даль и Нюгорд ввели ключевое слово class для описания шаблона объектов моделирования — за это они получили премию Тьюринга в 2001 году. В Simula декларация уже поддерживала наследование и виртуальные методы.

Полностью объектную парадигму сформулировал Алан Кей в языке Smalltalk-72, созданном в Xerox PARC. Кей ввёл принцип «всё есть объект» и придумал сам термин object-oriented programming. По данным официального сайта Smalltalk (smalltalk.org), в Smalltalk-72 даже целые числа являются объектами и обмениваются сообщениями. В Smalltalk-80 модель декларации типов достигла зрелости и легла в основу Objective-C, Ruby, Java и Swift.

Кей вспоминал в докладе на HOPL-III (ACM, 2007), что идею сообщений между сущностями он подглядел у биологов, наблюдая за клетками. Каждая клетка — независимый вычислитель, обменивающийся сигналами. Отсюда фундаментальный принцип инкапсуляции и позднего связывания.

Как объявить шаблон и создать экземпляр — пошагово

Процедура одинакова в большинстве языков: сначала описать декларацию, затем через специальную операцию создать экземпляр. Ниже подробная пошаговая инструкция для трёх языков.

Шаг 1. Определите декларацию с помощью ключевого слова — class в Java, Python, C++. Далее укажите имя с заглавной буквы согласно соглашениям PascalCase, зафиксированным в Google Java Style Guide и PEP 8.

Шаг 2. Опишите поля и методы. В Java поля объявляются с модификаторами доступа private, public, protected. В Python поля определяются в методе __init__. В C++ используйте секции public и private.

Шаг 3. Создайте экземпляр. В Java: Car c = new Car();. В Python вызов декларации как функции: c = Car(). В C++ на стеке: Car c; или в куче: Car* c = new Car();.

Шаг 4. Обратитесь к полям и методам через точку: c.speed = 60;. Далее вызывайте методы: c.start();. В C++ для указателя используйте оператор стрелки c->start();.

Шаг 5. Удалите экземпляр. В Java и Python освобождение памяти выполняет сборщик мусора. В C++ явно вызовите delete для сущностей, созданных через new, иначе получите утечку памяти.

Реализация в Java, Python и C++

Различия в реализации связаны с моделью памяти и типизацией. В Java декларация всегда лежит в отдельном .java файле, компилируется в .class и загружается ClassLoader. Каждый объект хранится в куче JVM. По данным Oracle JDK 21 Runtime Reference, минимальная сущность в Java занимает 16 байт заголовка плюс поля с выравниванием по 8 байт.

В Python любой класс сам является объектом типа type — эта метамодель описана в PEP 3115. Тип создаётся во время выполнения, и его атрибуты можно менять динамически: Car.color = ‘red’. В CPython 3.12 минимальный размер экземпляра составляет 16 байт для наследников object. Такой подход отличается от Java: там метамодель фиксирована и менять её нельзя без java.lang.instrument.

В C++ декларация близка к структуре, но по умолчанию поля класса private, а поля struct — public. Стандарт C++20 §11 разрешает экземпляру находиться на стеке, в статической памяти или в куче. Это отличается от Java, где объекты бывают только в куче, и от Rust, где вместо class используются struct с методами impl.

Класс vs объект: сравнение с интерфейсом и структурой

Четыре формы описания типа часто путают. Разница между ними принципиальна и закреплена в спецификациях языков.

Класс против объекта. Первый — статический шаблон, второй — динамический экземпляр. В отличие от объекта, декларация не занимает памяти под данные экземпляра. Одна декларация порождает миллионы экземпляров в куче.

Шаблон против интерфейса. Интерфейс описывает контракт без реализации. По сравнению с шаблоном, интерфейс в Java до версии 8 не содержал кода — только сигнатуры методов. Декларация реализует интерфейс через implements, а экземпляр интерфейса создать невозможно.

Шаблон против структуры. В C++ struct и class отличаются только уровнем доступа по умолчанию. В C# структура — value type на стеке, декларация — reference type в куче. В отличие от них, Go классов вообще не имеет — вместо них struct с методами.

Ограничения ООП-модели

Существует набор жёстких ограничений, которые нельзя обойти без изменения языка. Нельзя вызвать нестатический метод экземпляра до его создания — это гарантирует JVM через NullPointerException. Согласно JLS §15.12, метод экземпляра требует ссылку this, которой нет у неинициализированной декларации.

Нельзя создать экземпляр абстрактной декларации напрямую. Такая сущность в Java и C# не инстанцируется через new — компилятор выдаст ошибку. Единственное исключение — статические методы: они привязаны к самой декларации и вызываются как ClassName.method() без создания экземпляра.

Нельзя произвольно менять размер экземпляра после инициализации: в Java и C++ поля фиксированы декларацией. В Python поля можно добавлять динамически, но это не гарантирует работу оптимизаций CPython, и такой стиль не рекомендуется в PEP 8.

Мифы и заблуждения об ООП

Вокруг темы сложилось несколько устойчивых заблуждений — они не подтверждаются первичными источниками.

Класс и объект — одно и то же

Нет. Класс порождает объект, но не совпадает с ним. Первый — тип, второй — значение этого типа. Смешение возможно только в языках с метаклассами (Python, Ruby, Smalltalk), где сама декларация тоже является объектом типа type. Но и там сущность-описание и её реализация — разные вещи.

Без ООП программу написать нельзя

Не соответствует действительности. Процедурные языки (C, Fortran, Rust без trait-объектов) обходятся без деклараций типов в ООП-смысле. ООП — один из подходов, а не обязательный стандарт. По данным TIOBE Index 2025, доля процедурных языков в промышленной разработке составляет около 25%, и она стабильна последние 10 лет.

Вопрос-ответ

Чем декларация типа отличается от экземпляра простыми словами? Класс — это чертёж дома, объект — сам построенный дом. Один чертёж используется для тысяч зданий, каждое со своим адресом и цветом стен. Так же одна декларация порождает миллионы экземпляров в памяти.

Можно ли создать экземпляр без декларации типа? В классических ООП-языках (Java, C++, C#) нельзя — любой объект всегда принадлежит какому-то классу. В JavaScript и Lua возможны безклассовые объекты (prototype-based), но и там есть неявный прототип.

Как класс превращается в объект? Через оператор создания — new в Java и C++, вызов декларации как функции в Python. Runtime выделяет память под поля, вызывает конструктор и возвращает ссылку на новый объект.

Что появилось раньше — класс или объект? Понятие класса ввели Даль и Нюгорд в Simula 67 в 1967 году. Термин object-oriented programming появился позже, в Smalltalk-72, у Алана Кея. То есть шаблон исторически старше самой парадигмы.

Оцените статью
uchet-jkh.ru
Добавить комментарий