Не работаю я в GameDev (к сожалению, в Ярославле серьёзная разработка GameDev отсутствует).
Указатели в API когда торчат наружу — это всегда плохо. Указатель по определению может быть nullptr, а значит мы заставляем программиста проверять результат, чего он разумеется может и не сделать.
Просто shared_ptr удобен для примера, на котором я объяснял суть copy-on-write подхода. В принципе для однотипных или однородных объектов есть возможность например использовать placement new используя память объекта-владельца (например запись выборки и поля этой записи).
Я пишу на C++, всегда на нём писал, не считая десяток других языков (включая C#) которыми я пользовался для решения как правило локальных специфических задач (типа поправить скрипты сборки на Perl или обернуть сборку C++ через MC++ для C#/VB.NET). Проекты мы всегда писали на C++, но некоторое время назад понадобилась скриптовая обвязка. Всё отлично работает в C++ без shared_ptr, да и с ним всё замечательно работает. C++ тем и хорош, что нет никаких догм разработки, периодически выходит очередная идиотская книга, где всех учат как «правильно» программирость на C++, после чего по компании проносится очередная эпидемия каких-то немыслимых private-virtual методов или фабрик, которые создают фабрики. В целом C++ нас вполне устраивал, но некоторые вещи лучше заскриптовать, проверить и иметь возможность их быстро править. Что-то совсем прикладное. Здесь нам помог Python и связка Boost.Python. Не везде требуется сетевое взаимодействие, там где оно не нужно, преимущества реактивного исполенения кода написанного на C++ ни с чем не сравнимо.
Ссылки, особенно константные — это здорово, как и размещение на стеке, управление памятью через размещающий new. В общем C++ особо и не выбирали, мы на нём просто без вариантов пишем. Python просто для прикладников иногда очень удобен.
Для plain C есть специальная техника работы через handle объекта (указатель на forward declared struct, у которой единственный член — объект оборачиваемого класса, сама структура описывается в .cpp, где уже можно использовать #include C++ классов) и дублирование методов экспортируемыми extern «C» функциями. В целом в других языках есть удобные биндинги: JNI, MC++, Boost.Python, Rice и т.п., но если совсем край и надо чистый Си, то нужно для Си делать API отдельно в рамках wrapping-процесса, тут уж придётся делать downgrade типов.
Ну тот же std::string вполне можно вернуть, не возвращать же const char*. Или с std::vector или std::deque работать, не принимать же странный element* с количеством отдельно. Совсем уж до POD-типов зачем себя ограничивать. Тот же std::map или std::set замучаешься сам реализовывать. Вряд ли кто-то в свободное время балуется реализацией красно-чёрных деревьев или например тот же std::unordered_map, его вообще проблематично заменить.
Потому что нужна максимальная скорость на критичных участках. Но это не отменяет того факта, что пользователю системы нужно удобное API, вне зависимости от того, что твориться в движке. Это как автомобиль. Машина должна быть красивой, хорошо ездить, удобно управляться и не должна требовать каких-то навыков сверх навыка вождения.
Ну статья от 99-го года, а CoW жив и по сей день. Массовое копирование при многопоточном доступе также будет причиной плохой производительности. Безопаснее вернуть std::string вместо ссылки на него, опять же как это сказано в статье, это освобождает от обязательств в реализации и завязки на хранение именно std::string. Если нет CoW, то при копировании из поля класса в значение результата будет довольно дорогое копирование, сложность которого невозможно померять, так как строки произвольной длины. Есть всевозможные оптимизации строк, именно по причине дорогого копирования, но как раз в данном случае мы получим проседание по производительности при честном копировании строк.
Тем не менее реализация GCC std::string сделана на основе copy-on-write, за что мы и любим GCC, можно по сорцам увидеть ref_count. Насчёт стандарта Вы правы, всё верно, не должно быть CoW в std::string, тем не менее данные шарятся между несколькими строками. Завязываться на это нельзя разумеется, потому что например реализация STL от Microsoft сделана по стандарту с честным копированием. Для любителей гарантированного CoW с возможностью декодирования и прочими плюшками есть QString. Если честно, решительно не понимаю за что Вы так не взлюбили copy-on-write подход.
Я тоже сперва так думал, позже оказалось что я экономил на спичках, так что std::shared_ptr можно спокойно копировать, в большинстве случаев 99% времени съедают сетевое взаимодействие (с той же БД или удалёнными серверами с бизнес-логикой по RPC) либо какие-нибудь адовые алгоритмы коллег по цеху. В C++ не хватает кстати асинхронности и транзакционности данных, но в целом при должном умении на C++ можно создать систему любой сложности с безграничными возможностями для оптимизации. Что до частых выделений памяти и её фрагментации, то с этим можно бороться выделяя общие сущности, размещая в них более мелкие, так например для полей выборки в каждой записи можно через placement new разместить все данные результата запроса, это сэкономит время на выделение на порядок меньшего числа памяти, но в целом сами записи в выборке видимо придётся хранить именно по ссылке на данные через shared_ptr или copy_on_write, просто потому что на бизнес-логике часто идёт дообработка выборки с базы данных. Это по первому пункту.
По второму: поведение ничем не отличается от стандартного, разве что копирование произойдёт позже. Заставлять разработчика оперировать указателями, имхо плохая практика, если это обычные указатели, любые операции с ними небезопасны, если это указатели умные, то операции с ними громоздки и лучше всю дополнительную работу с умными указателями сразу завернуть в класс, с которым пользователь будет работать уже без ошибок.
Да ну что вы, я очень люблю C++ и замечательно умею его готовить. «Ужасное переусложнение» никто не увидит, если не заглянет в код реализации. Что до Copy-on-write, то этот подход в том же g++ применяется для std::string(!) что неплохо оптимизирует всевозможную работу с текстом. Почти везде CoW используется в Qt, тот же QString не копирует потроха с текстом при копировании объекта. По-вашему у Qt плохая архитектура?
Поддерживаю, Qt отличная библиотека не только сама по себе, но и как набор инструментов для написания своих библиотек. Но увы, иногда завязка на Qt бывает избыточной.
Для середнячка как раз статья нужная, поскольку часто они работают в команде при разработке серьёзной либы, а иногда пишут что-то своё в порыве альтруизма на волне своих локальных успехов. Уж извините, что не угодил продвинутому «прогеру», но для PRO-уровня писать дело неблагодарное, аудитория узкая и перманентно враждебно настроенная. В любом случае спасибо что снизошли до уровня убогого автора, пишущего никчёмные статьи. Я старался как мог сделать статью и понятно простой и полезной широкой аудитории. Увы мне, если не вышло.
Ну без классов умных указателей, потоков с мьютексами, без стандартных контейнеров массивов, множества, маппинга, да даже без всем известного базового исключения будет как-то трудно разрабатывать понятную библиотеку. К тому же что будем кидать в виде исключения, throw 1, если только struct и POD-типы? Для языка Си можно сделать отдельный интерфейс, обернув данные в структуры с forward declaration в .h (с обычным вложением объекта как единственного поля в структуре в .cpp-файле) и продублировав методы аналогичными extern «C» функциями, но это исключительно для случаев когда нужен чистый Си и для него отдельно выносится API. Я понимаю что любой STL класс можно реализовать самому, но стандартные реализации довольно хороши и всем известны, STL есть везде, чем вам не нравится использование std:: классов?
Не совсем, речь идёт о разделении данных и интерфейса. К тому же мне не нравится подход описанный как Pimpl (указатель на реализацию), если говорить о том, что в С++ в .cpp выносится имплементация, то она и так выносится в .cpp-файл, для того и нужно разделение .h/.cpp. Подход переноса описания данных в реализацию и разделения именно данных от интерфейса класса мне ближе, он берёт своё начало в старом-добром языке Си, когда структура описывалась forward declatation и использовалась как handle сродни this в С++, т.е. передавалась первым параметром в функции, который работали на манер методов в С++. Для примера можно посмотреть API WinPCAP… Как видите я везде избегаю нелюбимого мной понятия impl и данные класса описал именно как данные: class data. Да и copy_on_write шаблон интуитивно понятно что он работает с данными класса, а не с его реализацией, копируется же не реализация, а данные объекта.
Вы сначала запрограммируйте искусственный интеллект так, чтобы он не падал ни при каких обстоятельствах, не зависал и эффективно развивался, не превращаясь из человекоподобного разума в тыквообразный. Теоретики.
В прошлый раз все было по высшему разряду, включая как доклады, так и афтепати с мафией с Роначером. Докладчики снова на уровне. Придется ехать в ваш замечательный город снова.
Вот-вот, сделайте свой Google Glass, если уж он «так дёшево стоит». Я как бы намекну, что кроме наличия железа должна быть софтина, калибровка, тестирование, отладка, целый производственный цикл в области где не ступала нога человека.
Указатели в API когда торчат наружу — это всегда плохо. Указатель по определению может быть nullptr, а значит мы заставляем программиста проверять результат, чего он разумеется может и не сделать.
Просто shared_ptr удобен для примера, на котором я объяснял суть copy-on-write подхода. В принципе для однотипных или однородных объектов есть возможность например использовать placement new используя память объекта-владельца (например запись выборки и поля этой записи).
Ссылки, особенно константные — это здорово, как и размещение на стеке, управление памятью через размещающий new. В общем C++ особо и не выбирали, мы на нём просто без вариантов пишем. Python просто для прикладников иногда очень удобен.
По второму: поведение ничем не отличается от стандартного, разве что копирование произойдёт позже. Заставлять разработчика оперировать указателями, имхо плохая практика, если это обычные указатели, любые операции с ними небезопасны, если это указатели умные, то операции с ними громоздки и лучше всю дополнительную работу с умными указателями сразу завернуть в класс, с которым пользователь будет работать уже без ошибок.