На самом деле он используется в C ++ для всех этих типов наследования not-is-a (например, наследование реализации, mixins, boost :: noncopyable, где вы наследуете ее и делаете свой класс некопируемым, boost :: operator где вы наследуете от некоторого класса, и это добавит несколько операторов в ваш класс). Кроме того, при выполнении метапрограммирования с типами C ++ наследование от другого типа (типов) является одним из самых простых методов составления типов.
Другим случаем, когда вы наследуете от класса, который не имеет виртуальных функций, но служит «своего рода» интерфейсом, является статический полиморфизм (такие методы, как CRTP, где класс ConcreteA: BaseA). Им не нужны виртуальные функции, потому что все решается во время компиляции.
Однако, если вы хотите полиморфно относиться к классу во время выполнения, вам действительно нужно сделать хотя бы один метод виртуальным, и это будет деструктор. Есть исключения, но редко.
Даже если у вас полиморфная иерархия, вы иногда наследуете от конкретных классов. Одним примером будет SingleLineEdit, производный от TextEdit. Однако это немного опасно, потому что это нарушает инкапсуляцию родительского класса (например, его методы могут ожидать некоторые детали реализации, и вы должны уважать и сохранять их в подклассе - что может быть непросто, поскольку они являются деталями реализации, которые могут изменить без уведомления в следующей версии и т. д.)