1 | |||||||
С++ идиомы31.07.2016, 19:20. Показов 115269. Ответов 34
Метки нет (Все метки)
Перевод статей 1 и 2. Будет постепенно обновляться. Желающие внести вклад могут писать в ЛС.
Переведенные идиомы: self-assignment in an assignment operator Scope Guard Shrink-to-fit Checked delete Pointer To Implementation Получение адреса(ака взятие адреса aka Address-of) nullptr Iterator Pair Coercion by Member Template
14
|
31.07.2016, 19:20 | |
Ответы с готовыми решениями:
34
С++ идиомы - обсуждение Как и какие идиомы и паттерны можно (и лучше) применять? Идиомы программирования Английские идиомы: как правильно перевести in its own right? Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |
01.08.2016, 11:33 | 2 | |||||
Scope Guard
Задачи: Гарантировать освобождение ресурсов при возникновении исключения, но не освобождать ресурсы при нормальном завершении. Предоставить базовую гарантию безопасности исключений. Мотивация: Идиома RAII позволяет захватывать ресурсы в конструкторе и освобождать их в деструкторе, при достижении конца области видимости или из-за исключения. В RAII ресурс освобождает всегда. Это может быть не очень гибким решением. Может потребоваться освободить ресурсы в случае возникновения исключения и не освобождать при нормальном завершении. Решение и пример кода: Типичная реализация идиомы RAII с проверкой необходимости освобождения ресурса.
9
|
01.08.2016, 11:54 [ТС] | 3 | ||||||||||
self-assignment in an assignment operator (самоприсваивание в операторе присваивания)
T::operator= который обрабатывает случай, где левый и правый операнды являются одним и тем же объектом.
Понимайте разницу между идентичностью (левый и правый операнды являются одним объектом) и одинаковостью (левый и правый операнды имеют одинаковые значения).T::operator= должен защищать себя в случае индетичности объектов, код присваивания может быть удобным и безопастным предполагая, что работает с разными объектами. Существуют и другие техники, которые в некоторых случаях могут быть более удобными, но они не применимы во всех ситуациях. Например если все члены класса T (скажем mem1, mem2, ..., memN) предоставляют функцию swap(), то можно использовать следующий код
6
|
01.08.2016, 20:51 | 4 | ||||||||||
Shrink-to-fit (уменьшить до размеров)
Задачи: Уменьшить ёмкость (capacity) контейнера, до размера, достаточного для содержания элементов. Также известен как "Swap-To-Fit", введенный Скотом Майерсом в книге "Effective STL" ("Эффективное использование STL"). Мотивация: Контейнеры в стандартной библиотеке часто выделяют памяти под большее число элементов, чем находится в контейнере. Это позволяет оптимизировать расширение контейнера за счет более редких выделений памяти. Но, когда контейнер уменьшается (или когда запас израсходаван не полностью, прим. переводчика), память так и остается занятой, хотя, фактически, она не используется. Это не нужный перерасход памяти. Данная (shrink-to-fit) идиома была разработана чтобы уменьшить расход до минимального требуемого контейнеру количества памяти, тем самым экономя ресурсы. Решение и пример кода: Данная идиома очень проста, как показано ниже.
std::vector<int>(v) , создает временный вектор целых чисел, гарантируя, что выделенной памяти достаточно для хранения всех элементов вектора v, который передан в параметре. Также эти элементы копируются во временный вектор.Вторая половина инструкции - .swap(v) обменивает элементы вектора v и временного вектора, используя не выбрасывающую исключений функцию-член swap, что является очень эффективным средством, которое сводится к обмену внутренних указателей между векторами или чуть более того. После этого временный вектор удаляется, очищая память и удаляя элементы, которые первоначально находились в векторе v. Вектор v же имеет ровно столько памяти, сколько нужно для хранения элементов. ISO/IEC 14882:1998 не гарантирует такое поведение для конструктора копирования. Как гарантировать такое поведение? Более надежным решением (в частности std::string и std::vector могут быть реализованы с использованием подсчета ссылок и тогда конструктор копирования может "скопировать" всю избыточную память) будет использование конструктора диапазонов, вместо конструктора копирования:
В C++11 некоторые контейнеры предоставляют функцию-член shrink_to_fit, например vector, deque, basic_string. shrink_to_fit запрашивает уменьшение capacity до size, Но данный запрос не является обязательным к исполнению.
8
|
01.08.2016, 20:51 | 5 | |||||||||||||||||||||||||
Checked delete
Цель: Повышение безопасности при использовании delete expression. Мотивация и пример проблемного кода: Стандарт C++ (пункт 5.3.5/5) позволяет использовать в delete-expression указатель на не полный тип, но при этом, если деструктор или функция освобождения памяти объекта не являются тривиальными, то это приведет к неопределенному поведению.
Сообщение от 5.3.5/5
В следующем примере в main.cpp определяется объект типа Object. В функции main() вызывается функция delete_object(), определенная в deleter.cpp, где нет определения класса Object, а есть лишь его объявление. Вызов delete в данной функции приводит к неопределенному поведению.
Идиома checked delete полагается на вызов шаблонной функции для удаления объекта, а не на прямой вызов delete, который может привести к неопределенному поведению для объявленных, но неопределенных типов. Ниже приводится реализация шаблонна функции boost::checked_delete из Boost Utility library.Её использование вызывает ошибку компиляции при использовании sizeof для параметра шаблона T, если T - не полный тип. Если T объявлен, но не определен, то sizeof(T) будет генерировать ошибку компиляции или возвращать нулевое значение, в зависимости от компилятора. Если sizeof(T) вернет ноль, то произойдет ошибка компиляции, т.к. объявляется массив с отрицательным количеством элементов (-1). Имя type_must_be_complete в данном случае появляется в сообщении об ошибке и позволяет понять что произошло.
delete[] .Предупреждение: std::auto_ptr не использует никакого эквивалента checked delete. Поэтому инстанцирование std::auto_ptr с неполным типом может привести к неопределенному поведению в деструкторе, если в момент объявления std::auto_ptr тип параметра шаблона определен не полностью.
12
|
02.08.2016, 08:12 [ТС] | 6 | ||||||||||||||||||||||||||||||
Pointer To Implementation (pImpl)
Идиома "pointer to implementation" (pImpl) также называется "opaque pointer" (дословный перевод "непрозрачный указатель"), это способ предоставления данных и в еще один уровень абстракции в реализации классов. В С++ вы должны написать декларацию переменных-членов класса внутри определения класса, эти члены должны быть публичны (прим. переводчика - думаю речь идет об интерфейсе, иначе зачем членам быть публичными) и поскольку для членов выделяется память абстракция реализации не возможна для "всех" классов. Тем не менее, за счет дополнительного указателя и вызова функции, вы можете иметь такой уровень абстракции через указатель на реализацию. Допустим вы написали такой класс:
pImpl может реализовывать следующий паттерн таким образом, что описанная выше ситуация не является проблемой.
8
|
☆ Форумчанин(FSC)☆
|
|||||||||||
09.08.2016, 19:40 | 7 | ||||||||||
Получение адреса(ака взятие адреса aka Address-of)
Назначение: Поиск адреса обьекта класса который имеет перегруженный унарный оператор "амперсанд"(&). Интерес: Язык С++ разрешает перегрузку унарного амперсанда (&) для классовых типов. Тип возвращаемого значения не обязательно должен быть реальным адресом обьекта . Предназначение такого класса довольно спорно, но все же язык позволяет это.Идиома взятия адреса это способ получения реального адреса обьекта, независимого от перегруженного унарного амперсанда и его инкапсулированости. В примере ниже не удается скомпилировать функцию main потому что оператор & класса nonaddressable является закрытым членом этого класса.
Идиома получения адреса запрашивает адрес обьекта используя серию преобразований
Используется в библиотеке boost для получения адреса Эта функция уже пристутствует в заголовке <memory> нового стандарта C++(C++ 11)
2
|
161 / 153 / 92
Регистрация: 18.11.2015
Сообщений: 677
|
||||||||||||||||||||||||||||||||||||
16.08.2016, 15:46 | 8 | |||||||||||||||||||||||||||||||||||
Nullptr
Задачи: Научиться отличать число 0 от нулевого указателяМотивация: На протяжении многих лет С++ имело позорный недостаток, у С++ не было ключевого слова, которое бы обозначало нулевой указатель. С++ 11 избавился от этого недостатка. Строгая типизация С++ делает определение глобальной константы NULL в стиле языка C почти что бесполезной в выражениях, например:
Проблема самого первого примера в том, что С++ запрещает приведение из типа void * , даже когда его значение является константным нулем. Но, для простого константного нуля С++ имеет преобразование int в указатель (еще short в указатель , long в указатель и т.п.).Это имеет свои минусы. Приведем пример на работе с перегруженными функциями:
main превратиться в:
Код
Double * Int #define NULL 0 так, как задумывалось (с указателями), нужно всегда писать что типа static_cast<double *>(NULL) Использование #define NULL 0 имеет свою кучку проблем. Помимо проблемы с перегруженными функциями, C++ требует того, чтобы NULL было определено целочисленным константным выражением со значением 0. Поэтому, в отличии от С, нулевой указатель не может быть определен как ((void *)0) в стандартной библиотеке C++. Более того, конкретная форма определения оставлена для той или иной реализации, что значит, что 0 и 0L - подходящие определения, наряду с некоторыми другими.Решение и пример использования Идиома нулевого указателя решает некоторые вышеперечисленные проблемы и может быть использована снова и снова. Будущий пример является очень близким по функционалу решением, относительно ключевого слова nullptr , добавленного в С++ 11, и использует только стандартные приемы, которые были доступны еще до С++ 11.
#include класса выше)
nullptr с указателем на функцию-член (pmf ). Код выше успешно компилируется, если убрать строки с ошибками, которые мы допустили в демонстрационных целях (26, 31, 34, 35, 40, 41, 42, 50)Заметьте, что идиома нулевого указателя использует идиому Return Type Resolver, чтобы автоматически вывести нулевой указатель правильного типа, в зависимости от типа объекта, к которому мы его присваиваем. Например, если nullptr присваивают к char * , создается функция преобразования с char параметром шаблона.Последствия Есть некоторые недостатки этого приема, в список этих недостатков входят следующие пункты:
5
|
Мозгоправ
|
|||||||||||||||||||||
03.11.2019, 18:47 | 9 | ||||||||||||||||||||
Iterator Pair
Итераторная пара Задача Определить диапазон значений данных, без привязки к базовой структуре, которая используется этими значениями. Также известно как Иногда это называют итераторным диапазоном (Iterator Range). Проблема Хорошо известно, что для создания вектора vector<int> из другого vector<int> используется конструктор копии. Также для создания вектора vector<int> из другого vector<int> можно использовать идиому Приведение типа посредством шаблонного метода (Coercion by Member Template), применённую к конструктору шаблонного члена. Пример кода показан ниже.
Решение и пример кода Пара итераторов используется для обозначения начала и конца диапазона значений. Благодаря шаблону проектирования итераторов, кто бы ни использовал эту идиому (в нашем примере для вектора), может получить доступ к диапазону, абстрагируясь от деталей реализации структуры данных. Единственное требование заключается в том, что итераторы должны предоставлять минимальный фиксированный интерфейс. Например, предоставлять оператор префиксного инкремента.
set<T>::iterator или list<T>::iterator или массив POD. Независимо от типа, любой общий алгоритм, написанный в терминах итераторных пар, работает. Часто полезно показать, что модель должна реализовывать итераторные типы. В приведенном выше примере итераторы требуют от модели наличия как минимум InputIterator . Более подробная информация о категориях (тегах) итераторов и их использовании описана в идиоме Диспетчеризация тегов.Иногда без использования идиомы итераторной пары обойтись невозможно. Например, при создании std::string из буфера символов, в котором присутствуют нулевые символы, применение идиомы итераторной пары просто необходимо.
Все стандартные контейнеры. Связанные идиомы
1
|
Мозгоправ
|
||||||||||||||||
05.11.2019, 14:53 | 10 | |||||||||||||||
Coercion by Member Template
Приведение типа посредством шаблонного метода Задача Повысить гибкость интерфейса шаблонного класса, позволяя шаблону класса участвовать в тех же неявных преобразованиях типов, что и его типы-параметры. Проблема Часто бывает полезно расширить отношения между двумя типами до специализации шаблонов классов с этими типами. Например, предположим, что класс D наследуется от класса B . Указатель на объект класса D может быть присвоен указателю на класс B . C++ поддерживает это неявным образом. Однако типы, составленные из этих типов, не разделяют отношения родительских типов. Это относится и к шаблонам классов, поэтому объект Helper<D> обычно нельзя присвоить объекту Helper<B> .
std::unique_ptr<D> в std::unique_ptr<B> . Это интуитивно понятно, но не поддерживается без использования идиомы Приведение типа посредством шаблонного метода.Решение и пример кода Определите в шаблонном классе шаблонные методы, которые полагаются на неявные преобразования типов, которые поддерживаются типами параметров. В следующем примере шаблонный конструктор и оператор присваивания работают для любого типа U , для которого разрешена инициализация или присвоение T* из U* .
D унаследован от класса B , D-объект является B-объектом. Однако массив объектов D не является массивом объектов B . Это запрещено в C++ из-за возможного неправильного вычисления размера массива на основании размера элемента. (Объект производного класса, скорее всего, будет занимать больше памяти, чем объект базового класса. Поэтому если предположить, что массив D-объектов, это массив B-объектов, то часть массива будет утеряна при расчёте объёма занимаемой памяти, исходя из размера B-объекта, а остальные объекты, за исключением первого, будут некорректны из-за неправильного позиционирования на начало объекта. – прим. перев.) Ослабление этого правила для множества указателей может быть полезным. Например, массив указателей на D должно быть возможно присвоить массиву указателей на B (при условии, что деструктор B является виртуальным). Этого можно достичь с помощью рассматриваемой идиомы, но требуется дополнительная осторожность, чтобы предотвратить копирование массива указателей на базовый тип в массив указателей на производный тип. Для этого можно использовать специализации шаблонных методов или SFINAE.В следующем примере используется шаблонный конструктор и шаблонный оператор присваивания. Из-за объявления Array<U *> они будут разрешать копирование массивов указателей только тогда, когда типы элементов различаются.
std::unique_ptr и std::shared_ptr , используют эту идиому.Предостережения Типичной ошибкой в реализации идиомы Приведения типа посредством шаблонного метода является отсутствие нешаблонного конструктора копирования или нешаблонного копирующего оператора присваивания при наличии шаблонного конструктора копирования и шаблонного копирующего оператора присваивания. Компилятор автоматически создаёт конструктор копирования и копирующий оператор присваивания, если класс не объявляет их, что может вызвать скрытые и неочевидные ошибки при использовании этой идиомы. Известные применения
Связанные идиомы
1
|
329 / 149 / 33
Регистрация: 29.06.2019
Сообщений: 1,429
|
|
12.02.2021, 16:38 | 12 |
доп. на habr'e
----------- добавлю линк на pdf - про Exception Safety, включая RAII - Using Exceptions | C++ - using RAII] for cleanup ... вообще Exception Safety - очень интересная тема - очень нравится идея ===== Добавлено через 11 минут CRTP, насколько понимаю... но есть один нюанс - чтобы, действительно, добиться полиморфного поведения от объектов классов, создаваемых на основе шаблона во время компиляции, - т.е. чтобы их можно было помещать в контейнер<T*>, - надо всё-таки ещё создать абстрактный класс, от которого и унаследовать шаблон... - см. Polymorphic copy construction... поэтому нужен вам или не нужен этот шаблон - это ещё вопрос... хотя заменить динамический полиморфизм статическим (там где это возможно) - вобщем-то неплохо для app performance Добавлено через 28 минут ===== спорная трактовка pImpl (точнее перевод на рус.яз.)... обычно private сектор класса можно оформить отдельным классом и использовать указатель на него в основном классе, а подключить его обычным #include impl.h (интерфейс), понятное дело, у которого может быть свой .c (реализация)... такое сокрытие private полей и методов (кстати, понятное дело, что будет больше кода) , действительно, полезно (писать этот ворох кода), когда, например, профилировщик класса показывает, что создание его (например, с большим количеством полей) является узким местом в вашем коде... в частности, при разработке библиотек и тем более кросс-платформенных - разработчику быстрее будет перекомпилировать проект если разделены зависимости между private и public частями основного интерфейса (.h-файл) и изменения внесены только в реализацию скрытого класса... то и не будут перекомпилироваться все пользователи его, а их может быть несколько, в частности при разработке кроссплатформенной версии библиотеки... в Qt часто используется идиома pImpl для Widget'ов, например, для описания родительского окна и его потомков... в используемом мной U++ это не совсем актуально в таком разрезе, поскольку наследования ЭУ как такового нет (они все независимы - главное правильно расставлять конструкторы и деструкторы, согласно RAII, полагаю)... имхо и кстати, используя pImpl, - вы теряете возможность использовать inline (хотя последнего современные компиляторы в принципе воспринимают как попало)... и вижу пользу в использовании идиомы pImpl - например, при создании части интерфейса на C++ с выносом этой части в отдельную реализацию, чтобы основная часть основного класса, написанного на С, могла использовать этот свой stuff на С++ ... - т.к. правила дурного тона - смешивать C и C++ и неуважение к компилятору смешивать языки... имхо в общем, часто городить pImpl - это просто усложнять себе сопровождаемость кода, но обоснованное использование принесёт свои удобства... имхо Добавлено через 19 минут конечно, можно разделить интерфейс и реализацию и с помощью чисто абстрактного класса, но тогда мы вынуждены иметь дело с vtable в run-time'e ... но, используя идиому pImpl и уходя от необходимость иметь дело с vtable в run-time'e, улучшим ли мы performance нашего app, - спорно и не всегда, если нам всё равно приходится выделять доп. память под скрытый объект в придачу к основному объекту... + сокрытие части реализации в др. классе, требует подробного описания возможных побочных эффектов этой скрытой части на основной класс хотя бы в документации... вот как-то так Добавлено через 3 минуты ведь всегда интересно WHEN to use и каковы Последствия
0
|
Неэпический
|
|
14.02.2021, 12:22 | 13 |
Как-то странно не сказать о том, что программа должна быть еще и запущена перед тем, как будет выброшено исключение.
RAII - отдельный от исключений механизм, конечно же всегда и программа должна быть запущена и исключение перехвачено и всё остальное должно быть правильно. Что касается std::terminate, то он вызывается, но вот остальное зависит от реализации. http://eel.is/c++draft/except#handle-9
0
|
329 / 149 / 33
Регистрация: 29.06.2019
Сообщений: 1,429
|
|
14.02.2021, 13:08 | 14 |
ну не стоит же этого забывать - в др. языках всё, что угодно, и само может выскакивать - в более высокоуровневых... а тут в try{} catch(...){} надо не забыть обернуть, чтобы выскочило...
0
|
329 / 149 / 33
Регистрация: 29.06.2019
Сообщений: 1,429
|
||||||
14.03.2021, 16:53 | 16 | |||||
действительно, как-то разорвано всё на wiki... простое наследование от Астахова - в стиле CRTP с возможностью использовать объекты в полиморфном контейнере - рабочий вариант так:
Кликните здесь для просмотра всего текста
при этом передали rowPointer в std::unique_ptr и удалили при выходе из scope...
критические замечания всегда приветствуются... лучше кодом Добавлено через 19 минут и наблюдения:
0
|
фрилансер
5826 / 5346 / 1097
Регистрация: 11.10.2019
Сообщений: 14,284
|
|
14.03.2021, 20:26 | 17 |
утечка же
Добавлено через 2 минуты я бы поопасался тут делать rvalue ссылку
1
|
329 / 149 / 33
Регистрация: 29.06.2019
Сообщений: 1,429
|
||||||
14.03.2021, 20:36 | 18 | |||||
а я думала, что unique_ptr действует, как обёртка, когда помещаем в неё... и в debug на break_point после выхода из scope, вроде, не увидела левых pointers - вот и подумала, что всё норм.? ...
но, полагаю, можно и так
наверно, на pointer'ах rvalue ссылку лучше не делать - приму на заметку!... &
0
|
фрилансер
5826 / 5346 / 1097
Регистрация: 11.10.2019
Сообщений: 14,284
|
|
14.03.2021, 20:43 | 19 |
JeyCi, да, я слегка ошибся - у тебя запутанная архитектура ) Утечки не будет
но зачем такие пляски, если вместо new сразу создать умный указатель через make_unique? Зачем метод clone? Добавлено через 54 секунды в данном цикле нужно просто константную ссылку. А переместиться может не только поинтер, а объект любого класса Добавлено через 2 минуты ещё у меня опасение, что мы тут оффтопим
0
|
329 / 149 / 33
Регистрация: 29.06.2019
Сообщений: 1,429
|
|
14.03.2021, 21:05 | 20 |
потому что по тому примеру с wiki (куда оставляла линк) - там помимо template'a ещё и Abstract Class - чтобы нормально в полиморфный контейнер входило... в принципе там всё описано по линку, но примеры уж очень разорванные и потому некомпилируемые - поэтому и выложила рабочую версию...
часто CRTP описывается только с template'ом и наследниками - а дальше пляски и не всегда работает, как положено... в частности полиморфное поведение в полиморфном контейнере... ещё потестить можно обычные примеры, но этот про запас не помешает... возможно, в любом случае спасибо за ваш view
0
|
14.03.2021, 21:05 | |