Стоит ли в этом случае использовать вложенные классы?

Я работаю над набором классов, используемых для воспроизведения и записи видео. У меня есть один главный класс , который действует как открытый интерфейс, с методами , как play(), stop(), pause(), и record() т.д. ... Тогда у меня есть рабочая лошадка классы , которые делают видео декодирования и кодирования видео.

Я только что узнал о существовании вложенных классов в C++, и мне любопытно узнать, что программисты думают об их использовании. Я немного насторожен и не совсем уверен, каковы преимущества / недостатки, но они, похоже (согласно книге, которую я читаю), могут использоваться в таких случаях, как мой.

Книга предполагает, что в сценарии, подобном моему, хорошим решением было бы вложить классы рабочих лошадок внутри класса интерфейса, чтобы не было отдельных файлов для классов, которые клиент не предназначен для использования, и чтобы избежать любых возможных конфликтов имен? Я не знаю об этих оправданиях. Вложенные классы для меня - новая концепция. Просто хочу узнать, что думают программисты по этому поводу.

Ответов (10)

Решение

Я бы не стал использовать здесь вложенные классы. Что, если бы вы создали абстрактный базовый класс для «мультимедийного драйвера» для обработки внутреннего материала (рабочей лошадки) и отдельный класс для интерфейсной работы? Интерфейсный класс может принимать указатель / ссылку на реализованный класс драйвера (для соответствующего типа носителя и ситуации) и выполнять абстрактные операции над структурой рабочей лошадки.

Моя философия заключалась бы в том, чтобы сделать обе структуры доступными для клиента безупречно, только при условии, что они будут использоваться в тандеме.

Я бы сослался на что-то вроде QTextDocument в Qt. Вы предоставляете прямой интерфейс для обработки данных на «голом железе», но передаете полномочия такому объекту, как QTextEdit, для выполнения манипуляций.

звучит как случай, когда вы могли бы использовать шаблон стратегии

Один из способов решить, использовать ли вложенные классы, - это подумать, играет ли этот класс вспомогательную роль или свою роль.

Если он существует исключительно для помощи другому классу, я обычно делаю его вложенным классом. На этот счет есть масса предостережений, некоторые из которых кажутся противоречивыми, но все сводится к опыту и интуиции.

Иногда уместно скрыть классы реализации от пользователя - в этих случаях лучше поместить их в файл foo_internal.h, чем внутри определения общедоступного класса. Таким образом, читатели вашего foo.h не увидят то, что вы бы предпочли, чтобы они не беспокоили, но вы все равно можете писать тесты для каждой конкретной реализации вашего интерфейса.

Вы могли бы использовать вложенный класс для создания (небольшого) вспомогательного класса, необходимого для реализации основного класса. Или, например, чтобы определить интерфейс (класс с абстрактными методами).

В этом случае основным недостатком вложенных классов является то, что их сложнее использовать повторно. Возможно, вы захотите использовать свой класс VideoDecoder в другом проекте. Если вы сделаете его вложенным классом VideoPlayer, вы не сможете сделать это элегантным способом.

Вместо этого поместите другие классы в отдельные файлы .h / .cpp, которые затем можно использовать в своем классе VideoPlayer. Теперь клиенту VideoPlayer нужно только включить файл, объявляющий VideoPlayer, и ему по-прежнему не нужно знать, как вы его реализовали.

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

Ваш класс кодировщика / декодера звучит так, как будто он лучше соответствует шаблону стратегии

Одна из причин избегать вложенных классов - это если вы когда-нибудь намереваетесь обернуть код swig ( http://www.swig.org ) для использования с другими языками. У Swig в настоящее время есть проблемы с вложенными классами, поэтому взаимодействие с библиотеками, которые открывают любые вложенные классы, становится настоящей проблемой.

Мы столкнулись с проблемой с полу-старым компилятором Sun C++ и видимостью вложенных классов, поведение которых изменилось в стандарте. Это, конечно, не причина не делать вложенный класс, просто нужно знать, если вы планируете компилировать свое программное обеспечение на множестве платформ, включая старые компиляторы.

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

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

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

Вот дополнительная информация о шаблоне PIMPL (раздел 3.1.1).