QUOTE (Xenia @ Sep 5 2014, 00:08)

Интересный (для меня) вопрос вы затронули. Хочу спросить, известно ли вам, как на простом C это может быть реализовано? Сама хотела бы вставить функции во внутрь структуры, то C этого не ест. Можно, конечно, как вы сказали, вместо функций указатели туда прописать, но тогда им инициализация нужна, и вызовы таких функций будут "косвенными", путем разыменовывания указателей на функцию.
Реализовать можно по той же схеме, которую использует компилятор С++. Только толку от этого будет мало - всё - определение таблиц указателей на функции, указатели на эти таблицы, инициализацию таблиц и указателя, объявление прототипов и т.д. - придётся делать руками. А профиту ноль. Ведь рулез от виртуальных функций (методов) именно в том, что программисту не нужно заморачиваться на контроле соответствия объектов и их функций - всё работает само. В С уже тот факт, что нельзя отнаследовать одну структуру от другой, сводит почти все плюсы технологии к нулю. Но руками всё это можно делать, это будет своего рода паттерн проектирования. В плюсах это реализовывается так.
В каждом классе, где объявлена хотя бы одна виртуальная функция (метод), создаётся служебный указатель vtbl - это указатель на таблицу указателей на виртуальные функции объектов этого класса. Эта таблица создаётся отдельно и инициализирутся адресами виртуальных фукнций класса. При виртуальном вызове происходит не прямой вызов функции, а (vtbl->vtable[<offset>])(), т.е. по указателю vtbl производится обращение к таблице указателей, берётся адрес требуемой функции (по смещению <offset>) и уже по этому адресу производится вызов функции. В реальности, например, на MSP430 виртуальный вызов выливается в дополнительные две регистровые однотактовые команды.
Описанный подход гарантирует, что на рантайме будет вызван метод класса, к объекту которого применяется вызов функции. Ну, собсно, это основа динамического полиморфизма, позволяет строить иерархии классов, у каждого из которых свои методы. Следует отметить, что при всей мощи, красоте и эффективности подхода не надо его применять везде. У него своя "ниша". Например, он отлично ложится на реализацию GUI или его удобно использовать в парсерах коммуникационных протоколов и их стеках. Это просто средство. Которое рулит там, где оно эффективно. Очень часто начинающие по неопытности пытаются всё делать на методах, в результате получают сложные перекрёстные зависимости, оверхед и прочие проблемы.
QUOTE (Xenia @ Sep 5 2014, 00:08)

Но если поглядеть на то, как родной C++ компилирует, то там вызовы функций-членов класса прямые (конечно, если перед этим они не были объявлены виртуальными). Как такое удается сделать, если С++ считать "надстройкой" над C, которая может быть транслирована на C препроцессором? В ведь именно так оно и было, по крайней мере, раньше.
То, что С++ является надстройкой над С - глубокое заблуждение. С++ является надмножеством С, но это совершенно самостоятельный язык и его конструкции не могут быть "странслированы на С препроцессором". И компилятор С++ - совершенно отдельная программа, работающая по своим правилам. Из С++ на С странслировать код технически несложно, но это ни разу не препроцессор. И просто такая задача давно уже никому не нужна. Такой компилятор в истории был, он был написан самим Б.Страуструпом на начальном этапе разработке С++ и назывался "С с классами". Страуструпу было удобно использовать уже имеющиеся С компиляторы - это избавляло его от написания back-end и позволяло сосредоточиться на отработке правил нового языка.
QUOTE (Xenia @ Sep 5 2014, 00:08)

P.S. Сама я C++ обожаю, но при программировании МК стараюсь его избегать - отпугивает излишний "динамизм", когда многое неявным образом заводится в куче. А я привыкла к спартанским ресурсам, когда под кучу памяти жалко, и хотелось бы всё по максимуму держать в статике.
Напрасно опасаетесь. С++ - язык со строгой статической типизацией, ориентированный на разработку критичного к быстродействию и потреблению ресурсов кода. Он не заставляет программиста использовать механизмы, которые не подходят к задаче или с которыми программист недостаточно хорошо знаком. "Динамизм" в вашей программе на С++ может появится только по вашей воле.
Попробуйте начать с простого - замените какую-нибудь структуру классом, это позволит технически связать данные и функции, которые в программе на С были связаны только логически. Посмотрите кодогенерацию, вы увидите, что по эффективности кода это будет ровно то же самое (если не лучше). По мере использования этого подхода вы почувствуете удобства инкапсуляции и абстрации, когда ваши объекты будут недоступны извне для
любого использования, а только лишь для такого, которое определено в интерфейсах классов (открытые функции-члены). Попробуйте привязать логически классы к объектам реального мира - это занимательная вещь - разложить объект реального мира на составляющие, с целью определить, что является для него интерфейсом, а что представлением. И таким образом, процесс разработки программы преобразуется в новый подход, в котором будет присутствовать явный этап проектирования.
При некотором опыте вы сможете начинать разработку программы с рисования структурной схемы, где прямоугольники будут обозначать объекты, а стрелки связи между ними - открытые функции-члены (т.е. интерфейс классов). Это позволит сразу исключить некоторые логические ошибки и заложить правильную архитектуру программы - "скелет". Потом останется только разработать представление классов (закрытую часть). На деле, конечно, это всё не так просто, нужен опыт, но при желании он приходит очень быстро. После этого возвращаться к одному только процедурному программированию уже не захочется. К слову, процедурного программирования в С++ остаётся вдоволь - практически вся реализация функций - это оно и есть, т.ч. любителям С скучать не придётся.