Ресурс-прокси класс… C++ лучшие практики
У меня есть общая проблема - написать оболочку C++ для библиотеки C… мое основное хранилище - указатели C-Struct, которые всегда представляют класс C++, содержащий только ОДНУ поданные данные.
class MyClassC {
struct MyStructS * hdl
...
}
создание всех конструкторов, статических и методных функций - это НЕ проблема... моя проблема проектирования прямо сейчас... должен ли я использовать дизайн класса значений или дизайн класса указателей.
Значение класса всегда возвращает объект, а НЕ указатель:
класс значений имеет отношение ONE к MANY между MyStructS и MyClassC
MyClassC myMethod () {
...
return MyClassC(struct MyStructS *...);
}
Деструктор класса значений никогда не освобождает указатель struct MyStructS и в основном пуст
Деструктор значения - это статический метод или метод, обычно называемый destry или delete.
MyClassC::delete() {
DestroyMyPointer(&hdl)
}
...
MyClassC obj = {...};
...
obj.delete();
...
класс значения также является аргументом для методов значения:
some_proc_or_method (MyClassC arg1, MyClassC arg2, …) {
...
}
и есть другой вопрос:
Как создать аргументы по умолчанию для аргумента класса значения?
some_proc_or_method (MyClassC arg1, MyClassC arg2 = MyClassC {...} ???? ) {
...
}
Указатель класса всегда возвращает указатель объекта:
класс указателя имеет отношение ОДИН к ОДНОМУ между MyStructS и MyClassC
MyClassC* myMethod () {
...
return this or getThisFrom(myStructS_pointer)
}
Деструктор класса указателя всегда освобождает указатель struct MyStructS
~MqClassC () {
DestroyMyPointer(&hdl)
}
...
MyClassC* obj = new MyClassC(...);
...
delete obj;
...
1 ответ
Я бы выбрал вариант 3: тонкая обертка, которая в первую очередь определяет правильность владения. Код C уже полезен в C++. Единственная реальная выгода, которую можно получить при обертывании, - это правильная семантика владения. Оба ваших подхода пытаются возложить бремя отслеживания прав собственности на пользователя вашего API. Это не полезная оболочка, она просто добавляет лишний слой косвенности. Ваши клиенты по-прежнему должны учитывать внутреннюю часть C API, чтобы быть эффективными.
Вместо этого я предлагаю вернуть интеллектуальный указатель с четкой семантикой владения. Напишите несколько встроенных бесплатных функций, которые принимают умный указатель и перенаправляют на C API. Конечным результатом является то, что у вас есть почти тот же API (меньше кривой обучения) с семантикой владения C++. Это выигрыш, и результат будет менее громоздким.
#include <memory>
#include <CLibAPI.h>
namespace CPPWrapC {
namespace detail {
struct DestroyMyPointer {
void operator()(MyStructS *hdl) {
::DestroyMyPointer(&hdl);
}
};
}
using Handle = std::unique_ptr<MyStructS, detail::DestroyMyPointer>;
Handle myMethod() {
MyStructS *ret = /* init it */;
return Handle{ret};
}
inline void some_proc_or_method(Handle const& hdl) {
c_lib_some_proc_or_method(hdl.get());
}
}
Итак, теперь у вас есть простая оболочка, которая поддерживает дескрипторы с семантикой единого владельца. Именно то, что вы хотели, я думаю. И использовать его почти так же, как использовать оригинальный упакованный API, поэтому клиенты могут использовать все, что они знают о библиотеке C, для работы.
И это может служить трамплином. Теперь у вас есть тип C++, который вы можете включить в качестве члена для дальнейшего переноса.