У меня есть несколько классов в библиотеке с внутренними элементами, которые я хочу скрыть от кода клиента. С точки зрения клиента, каждый класс запрашивается из класса библиотеки и используется только как непрозрачный указатель. Пример заключается в следующем:Скрытие членов в открытом интерфейсе и ODR
struct SomeSystem;
void doSomethingToSomeSystem(SomeSystem* system, Parameters params);
void doSomethingElseToSomeSystem(SomeSystem* system, Parameters params);
На стороне реализации, SomeSystem имеет несколько пользователей, которые не являются видимыми для вызывающего абонента. Это все хорошо, но я не очень нравится неуклюжий синтаксис использования:
SomeSystem* system = lib->getSomeSystem();
doSomethingToSomeSystem(system, params);
doSomethingElseToSomeSystem(system, params);
Другой подход заключается в следующем:
struct SomeSystem;
namespace somesystem {
void doSomething(SomeSystem* system, Parameters params);
void doSomethingElse(SomeSystem* system, Parameters params);
}
С кодом использования:
SomeSystem* system = lib->getSomeSystem();
somesystem::doSomething(system, params);
somesystem::doSomethingElse(system, params);
я мог также используют глобальные методы, называемые doSomething
и doSomethingElse
, и зависят от перегрузки функций, если другой тип также определяет doSomething
. Однако в этом случае трудно найти всех «членов» SomeSystem в среде IDE.
я испытываю на самом деле использовать функции-члены:
struct SomeSystem {
void doSomething(Parameters params);
void doSomethingElse(Parameters params);
};
С кодом использования:
SomeSystem* system = lib->getSomeSystem();
system->doSomething(params);
system->doSomethingElse(params);
Последний фрагмент выглядит хорошо для меня, но SomeSystem больше не является непрозрачным указатель - он фактически определяет членов. Я немного опасаюсь этого. Одной из потенциальных проблем является одно правило определения. Однако «публичное» определение и «личное» определение класса будут видны только для разных единиц перевода. Есть ли какая-то другая беда, скрытая здесь? Если код клиента пытается создать экземпляр SomeSystem в стеке или использовать новый, это, очевидно, приведет к краху программы. Но я согласен с этим. Возможно, я смогу обойти это, предоставив частный конструктор в открытом интерфейсе.
Другим подходом, конечно же, является определение абстрактного класса с использованием виртуальных методов. Однако я бы хотел избежать накладных расходов на это, если это не является абсолютно необходимым.
EDIT:
Чтобы быть ясно, мне интересно, если это законно, чтобы иметь публичный заголовок, который клиент включает в себя содержащий другое определение класса (с некоторыми членами отсутствует), чем то, что используется реализация , так как клиент никогда не создает экземпляр класса.
Общественный заголовок:
struct SomeSystem {
void doSomething(Parameters params);
void doSomethingElse(Parameters params);
};
Частный Заголовок:
struct SomeSystem {
Member member;
void doSomething(Parameters params);
void doSomethingElse(Parameters params);
};
Частный источник (включая частный заголовок):
void SomeSystem::doSomething(Parameters params) {
...
}
void SomeSystem::doSomethingElse(Parameters params) {
...
}
Это работает, когда я проверить это, но я не уверен, если он каким-то образом нарушает стандарт.Два заголовка никогда не включаются в одну единицу перевода.
Вы знакомы с [Непрозрачный указатель/Ипподром Pimpl] (http://en.wikipedia.org/wiki/Opaque_pointer)? –
Да, я знаком с этим, и я бы предпочел избежать этого. Он вводит дополнительное выделение памяти и усложняет реализацию. – rasmus
Обратите внимание, что я специально после синтаксического сахара, не делая мое решение худшим качеством или сложностью. – rasmus