Иногда объявление интерфейса идет рука об руку с его реализацией, это делает одна команда. Тут проблем не может быть. Но, например, для plugin'а объявлять и использовать интерфейс может одна команда, а реализовывать интерфейс совершенно другая команда, у которой свой контекст при реализации интерфейса и он может не очень хорошо стыковаться с константностью. Например, доступ к свойству может быть реализован с использованием lazy evaluation.
Тут ничего не поделать, COM медленная (в смысле разработки) и печальная технология, C++17 уж точно не планировался для нее. Раньше COM объекты можно было писать на Visual Basic 6, Delphi. До сих пор можно писать на Qt. Это, конечно, побыстрее.
NVI это другая модель использования виртуальных, разновидность Шаблонного Метода Банды Четырех. Интерфейсные классы носят более технологический характер, с их помощью реализуется доступ к непрозрачному модулю. В C++ нет понятия интерфейс и его приходится моделировать с помощью виртуальных функций.
Ну, наверное, стоит уточнить: в русскоязычном мире, обсуждается перевод на русский язык. И как показывает опыт, словом «общепринятый» (тоже, кстати, пафосное слово) очень часто прикрываются личные вкусы и пристрастия автора.
Написать что-то принципиально новое на тему программирования довольно сложно. Моя цель была собрать и изложить все варианты, связанные с интерфейсными классами, так, что бы проектировщик мог сделать осознанный выбор. Моя практика показывает, что программисты очень редко правильно реализуют интерфейсные классы (хотя делают это достаточно часто), то есть для большинства данная статья содержит новый, ранее не знакомый материал и будет весьма полезна.
Для реализации COM на С++ существует специальная библиотека — ATL от Microsoft (входит в состав Visual Studio). По ней есть книги. Раньше у меня была отличная книга, где демонстрировалось создание COM объектов на C++ «руками», без всяких библиотек, но кто-то зачитал. Ну, вообще, COM уже не модная технология.
Ну на счет «дилетантский» я бы категорически возразил. Мейерса переводил и редактировал И.В.Красиков, авторитетнейший переводчик, он переводит и редактирует практически все книги по тематике C++ в издательстве «Вильямс» (Герб Саттер, Андрей Александреску, Николаи Джосаттис). Его переводы стали стандартом де-факто. В технической терминологии достаточно много тонкостей и не всегда первый попавшийся вариант перевода будет лучше.
Ну а теперь загляните в русский перевод этой книги. Там используется термин «интеллектуальный указатель». И в последнее время так переводят другие переводчики. На английском это всегда было smart pointer. При выборе перевода я, по возможности, стараюсь использовать широко известные публикации на русском.
Мне кажется, «умный» можно отнести к устаревшим терминам. В [Josuttis], [Meyers3] и ряде других последних книг, посвященных современному C++, используется термин «ителлектуальный».
Интерфейсы используются во многих языках программирования, и материалов по поводу того, зачем они нужны, тоже очень много. Но при их проектировании и реализации в С++ имеется много проблем, тонкостей, вариантов, связанных с особенностями языка С++. Разбору всего этого и посвящена статья.
Все изменения инкапсулированы в шаблоне ImplPtrBase. Вот немного упрощенный вариант (без удалителя): using SH = System::Runtime::InteropServices::SafeHandle;
using PtrType = System::IntPtr;
template <typename T>
public ref class ImplPtrBase : SH
{
protected:
ImplPtrBase(T* p) : SH(PtrType::Zero, true)
{
handle = PtrType(p);
}
Спасибо, что обратили внимания. SafeHandle это тяжелая артиллерия. Но для полноты картины, наверное, стоит привести вариант с его использованием. Для этого шаблон ImplPtrBase надо наследовать от SafeHandle, и немного переписать его. Все остальное остается без изменений.
Термин «родной», как перевод native, используется в переводе упомянутой книги Хогенсона, и, наверное, достаточно часто в других публикациях. Еще я встречал слово «собственный». Вообще, мнения по правильному переводу иногда переходит на уровень религиозных войн, возможно, я напишу когда-нибудь статью на эту тему.
Про ограниченность RAII я пишу прямым текстом, раздел 6 как раз и посвящен тому, как можно преодолеть эту ограниченность. Проблемы традиционного протокола создания/удаления объекта через конструктор и деструктор и методы их решения не обсуждал. Несомненно, что это интересная тема, но статья и так большая, пришлось себя ограничивать.
Я сейчас готовлю статью, где (в том числе) обсуждается потенциальная опасность ситуации, когда функции-члены генерируются компилятором. Про проблемы перемещения, генерируемого компилятором, подробно пишет Скотт Мейерс. Я горячий сторонник все делать явно.
Спасибо! В действительности самая первая версия этой статьи где-то 8 лет назад и появилась. Но пришел С++11 с его семантикой перемещения и многое пришлось пересмотреть, эта моя статья несомненно рекордсмен по продолжительности написания.
Спасибо! Искать «научную новизну» в текстах по программированию, мне кажется, не совсем правильно. А рассказ про RVO, RAII и даже семантику перемещения это не цель, а просто необходимый фон для освещения главной цели статьи: рассказать как правильно проектировать классы, управляющие ресурсами.
Спасибо, не обратил внимания! Но при кодировании я придерживаюсь правила: как можно меньше использовать всяких правил по умолчанию, так как они снижают читаемость кода и иногда довольно запутанны, что усугубляет ситуацию. Так, что я почти всегда использую конструкции "=default" и "=delete", даже если их можно опустить.
using SH = System::Runtime::InteropServices::SafeHandle;
using PtrType = System::IntPtr;
template <typename T>
public ref class ImplPtrBase : SH
{
protected:
ImplPtrBase(T* p) : SH(PtrType::Zero, true)
{
handle = PtrType(p);
}
T* Ptr() { return static_cast<T*>(handle.ToPointer()); }
bool ReleaseHandle() override
{
delete Ptr();
return true;
}
public:
property bool IsInvalid
{
bool get() override
{
return (handle == PtrType::Zero);
}
}
};