Предоставление доступа к классу из статической библиотеки

#c #oop #winapi #static-libraries

#c #ооп #winapi #статические библиотеки

Вопрос:

Я разрабатываю библиотеку синтаксического анализа, в которой я анализирую данные и сохраняю их в разных структурах данных. Дизайн библиотеки таков, что в ней будут классы dataProvider, Parser и DataStore. Хранилище данных является элементом класса dataProvider. Потребителю необходимо вызвать функции в dataProvider для анализа файла и извлечения данных из библиотеки.

Теперь, если я предоставлю класс dataProvider, мне нужно также предоставить класс хранилища данных, который предоставляет детали реализации потребителю. Каков альтернативный способ предоставления доступа к функциям класса dataProvider? Должен ли я предоставлять такие функции, как LoadFile, GetRecords и создавать объект dataProvider глобально внутри cpp?

Комментарии:

1. Если ваше решение включает глобальную переменную, то это плохо.

2. Вызывает ли здесь беспокойство то, что кто-то может создать DataStore объект и использовать этот объект вне контекста a DataProvider ?

3. @DeadMG: std::cout тогда это плохое решение? В конце концов, это глобальная переменная.

Ответ №1:

Если пользователю не нужно использовать DataStore напрямую, его лучше не раскрывать. Вы можете добиться этого, создав абстрактный «интерфейс» DataProvider , содержащий только общедоступные чисто виртуальные функции. Внутренне имейте a DataProviderImp , который будет наследоваться от DataProvider , и содержать все необходимые определения и члены, которые являются частью фактической реализации.

Позвольте пользователю работать только с абстрактным классом. Таким образом, вы перетаскиваете в свой API только минимальные зависимости.

Ответ №2:

Во-первых, сведите к минимуму информацию, которая должна быть раскрыта в заголовках, поэтому используйте прямые объявления, ссылки и указатели, где это возможно (поэтому требуется только прямое объявление), и попробуйте использовать идиому PIMPL, чтобы скрыть реализацию.

Затем вы можете создать другой класс, который является фасадом системы, который закрывает точки входа в функции, и использовать косвенные методы для доступа к содержащимся элементам (например, к элементам данных, доступным извне), таким как дескрипторы и индексы.

Ответ №3:

Если проблема заключается в том, что внешние пользователи могут создавать свои собственные DataStore объекты, использовать их вне контекста a DataProvider , и они жалуются, что ваша библиотека не работает, есть простое решение и некоторые не очень простые решения.

Простое решение: документ, который DataProvider является внешним интерфейсом для вашей библиотеки. Это решение используется в стандартной библиотеке C и в Boost. Интерфейсом к std::map является файл заголовка <map> . Файлы реализации, которые создает этот заголовок #includes , и базовые типы данных, которые <map> создают его вспомогательные заголовки, — это не ваше дело. Вы должны использовать только std::map общедоступные интерфейсы. Используйте внутренние типы данных, и вы попадаете в мир неопределенного поведения. Подобные комментарии // The class DataStore is for internal use only. Use it and you will be fired. могут быть довольно мощным сдерживающим фактором.

Решение, которое не зависит от вышеизложенного: отгородите эти вспомогательные классы, определив их внутри DataProvider (например, у вас будет class DataProvider::DataStore ) и сделав эти определения классов закрытыми / защищенными DataProvider . Другой подход заключается в том, чтобы сделать все DataStore закрытым / защищенным и создать DataProvider дружественный класс.

Поскольку вы предоставляете статическую библиотеку и заголовки, всегда будут какие-то неприятные хакерские способы получить доступ к вашим базовым данным и методам, независимо от того, как сильно вы пытаетесь их отгородить. В какой-то момент «Это только для внутреннего использования. Убери свои грязные рукавицы!» подход имеет немало достоинств.

Комментарии:

1. Проблема в том, что если хранилище данных является переменной-членом, то я также должен указать заголовок для этого класса, который раскрывает большую часть моей внутренней структуры данных. Лучшее, что я думаю, это, как предложил eran, создать IDataStore интерфейса и создать для него элемент вместо конкретного класса, чтобы я мог предоставить заголовок, который имеет только интерфейс.

2. Если вы скрыли этот элемент от внешних пользователей, сделав его закрытым / защищенным, вас действительно волнует, могут ли внешние пользователи видеть ваши внутренние типы данных? Если вы пишете коммерческий пакет, возможно. Если это не так, в чем проблема? Даже если ваши внешние пользователи не могут получить доступ к вашим внутренним данным, предоставление ваших внутренних представлений может помочь вашим внешним пользователям понять ваш пакет.