Комментарии 29
А еще он не сможет создать этот StringBuilder на стеке
Точно так же как и плюсовый объект, для которого есть только ISomething и фабрика. Автор же не утверждает, что C круче - просто то, что в нём легко реализуется обычная схема инкапсуляции.
А что мешает создать конструктор, который возвращает StringBuilder*, а принимает указатель на память. Так извне можно аллоцировать буфер хоть на стеке, хоть на хипе. К тому же это лучше с точки зрения архитектуры - разделить ответственность получения ресурса(выделения памяти) и конструирования объекта.
и молиться, чтобы не забыть его при каких-либо изменениях в StringBuffer'е
Можно static_assert'ом проверить - надеюсь, про fastpImpl от Полухина все в курсе ) У него, правда, плюсы, но static_assert и в C11 есть, что то похожее можно сделать.
Покажите, как проще делается скрытие реализации с возможностью аллокации на стеке. private в C++ эту задачу не решает.
Что-то не вижу проблем (поправьте, если что):
size_t GetStringBuilderSizeof(); // Реализован где-то
В коде пользовательской функции:
StringBuilder* sb = (StringBuilder*)alloca(GetStringBuilderSizeof());
// Далее используем объект на стеке: явный вызов конструктора и т.д.
Способ я не предлагал. Но думаю вы правы в чем-то.
Можно разместить на стеке? - Да
Будет ли это удобно, как при обычном использовании стека? - Нет
У нас нет наследования
Стоило бы тогда рассказать про стандартную схему реализации полиморфизма (структура с указателями на функции).
В качестве альтернативы предлагают generic macro, которые даже можно автогенерировать с помощью макросной магии)
А можно поконкретнее? Как на них сделать, скажем, объект, который умеет read/write (и при этом иметь несколько реализаций, скрытых от пользователя)?
_Generic про параметрический полиморфизм. Обычно делают так:
- Пишут header-only реализацию контейнера, того же листа, например
- Пишут модуль, в котором конкретизируются структуры данных и методы. В этом случае read является _Generic макросом, который подставляется в зависимости от переданного типа, а сокрытие сделано как в описано в статье
Все вместе похоже на программирование на шаблонах в плюсах.
Вот примеры, лучше смотреть в том порядке, какой я предложил, есть разные подходы к реализации: [1], [2], [3]
На самом деле довольно грязно и сложновато в отладке, хоть и не как подход с void*. Поэтому есть несколько подробных статей, как сохранить типобезопасность ([4], [5]) и не острелить себе ноги [6]. Если мы широко используем X-macro, который включается без include guard и который легко забыть undef-нуть, становится еще интереснее)
Там можно еще делать структры с указательми на функции, и получаем методы. Которые можно "переопределить".
С одной стороны руками это достаточно напряжно, но в конце выглядит прикольно (и очень не читаемо).
Что напряжного в том, чтобы объявить несколько указателей в структуре, заполнить в функции инициализации и дальше вызывать obj->method(obj,...) ? Да, нет неявного this и в реализациях надо руками кастить объект к нужному типу - но всё равно дополнительной писанины не так много.
Сделать несложно. А вот понять что в конкретный момент там за указателем скрывается ...
Так весь смысл инкапсуляции - за указателем скрывается сущность с такими то методами, больше о ней ничего неизвестно. Конкретных реализаций несколько, могут добавляться уже после того, как написан использующий их код - так что закладываться на внутренности в принципе нельзя.
typedef struct StringBuilder StringBuilder;
самая идиотская вещь, которую смогли придумать Сишники. Эту структуру даже в умный указатель не возможно по нормальному потом завернуть... И всё это ради каких то приватных полей... для компилятора!
Какие умные указатели в C?
довольно часто делается линковка C++ кода с чисто-C либами
При этом надо сразу понимать, что несмотря на общую базу это разные языки с разыми подходами к реализации одних и тех же концепций, так что часто надо писать явный враппер.
правильно, все можно, хотя слово "можно" имеет много разных оттенков, мне к примеру без разницы на чем писать embedded sw, что действительно заботит - как и сколько долго придется систему тестировать и отлаживать, имея в виду довольно сложные сценарии для real time, по опыту для embedded С++ больших преимуществ не дает, конечно код можно сделать более читабельный и красивый, но отлаживать будет труднее, хотя многое конечно зависит от личных предпочтений
А собственно, что там невозможного, написать кастомный делитер?
#include <memory>
struct StringBuilderDeleter {
operator()(StringBuilder* ptr) const {
DestroyStringBuilder(ptr);
}
};
using StringBuilderPtr = std::unique_ptr<StringBuilder, StringBuilderDeleter>;
Язык С не создавался под ООП парадигму, и подобные конструкции лично на мой взгляд усложняют читаемость кода. Мораль - пишем на С++ если уж очень нужно ООП.
Мифы и реальность языка программирования C