Не пишите код, который зависит от порядка вычислений аргументов функции
Резюме
Порядок вычисления аргументов функции не определен, поэтому никогда не полагайтесь на то, что аргументы будут вычисляться в той или иной очередности.
Обсуждение
На начальных этапах развития языка C регистры процессора были драгоценным ресурсом и компиляторы решали трудные задачи эффективного их использования в сложных выражениях высокоуровневых языков. Для того чтобы позволить компилятору генерировать более быстрый код, создатели C дали распределителю регистров дополнительную степень свободы. При вызове функции порядок вычисления ее аргументов оставался неопределенным. Эта аргументация, вероятно, существенно менее важна в настоящее время, но главное, что порядок вычисления аргументов функций в C++ не определен и варьируется от компилятора к компилятору (см. также рекомендацию 30). В связи с этим необдуманные действия программиста могут привести к большим неприятностям. Рассмотрим следующий код: void Transmogrify(int, int);
int count = 5; Transmogrify(++count, ++count); // Порядок вычислений // неизвестен Все, что мы можем сказать определенного, — это то, что при входе в тело функции Transmogrify значение переменной count будет равно 7 — но мы не можем сказать, какой аргумент будет равен 6, а какой — 7. Эта неопределенность остается и в гораздо менее очевидных случаях, таких как функции, модифицирующие свои аргументы (или некоторое глобальное состояние) в качестве побочного действия: int Bump(int& x) { return ++x; } Transmogrify(Bump(count), Bump(count)); // Результат // неизвестен Согласно рекомендации 10, следует в первую очередь избегать глобальных и совместно используемых переменных. Но даже если вы благополучно устраните их, некоторый другой код может этого не сделать. Например, некоторые стандартные функции имеют побочные действия (например, strtok, а также разные перегруженные операторы operator<<, принимающие в качестве аргумента ostream). Рецепт очень прост — использовать именованные объекты для того, чтобы обеспечить порядок вычислений (см. рекомендацию 13): int bumped = ++count; Transmogrify(bumped, ++count); // все в порядке
Ссылки
[Alexandrescu00c] • [Cline99] §31.03-05 • [Dewhurst03] §14-15 • [Meyers96] §9-10 • [Stroustrup00] §6.2.2, §14.4.1 • [Sutter00] §16 • [Sutter02] §20-21
Проектирование классов и наследование
Наиболее важный аспект разработки программного обеспечения — ясно понимать, что именно вы пытаетесь построить. — Бьярн Страуструп (Bjarne Stroustrup)
Какого вида классы предпочитает разрабатывать и строить ваша команда? Почему? Интересно, что большинство рекомендаций данного раздела вызваны в первую очередь вопросами зависимостей. Например, наследование — вторая по силе взаимосвязь, которую можно выразить в C++ (первая — отношение дружбы), и такую сильную связь надо использовать очень осторожно и продуманно. В этом разделе мы сконцентрируем внимание на ключевых вопросах проектирования классов — как сделать это правильно, как не допустить ошибку, избежать ловушек, и в особенности — как управлять зависимостями. В следующем разделе мы обратимся к Большой Четверке специальных функций — конструктору по умолчанию, копирующему конструктору, копирующему присваиванию и деструктору. В этом разделе мы считаем самой важной рекомендацию 33 — "Предпочитайте минимальные классы монолитным".
Ясно представляйте, какой вид класса вы создаете
Резюме
Существует большое количество различных видов классов, и следует знать, какой именно класс вы создаете.
Обсуждение
Различные виды классов служат для различных целей и, таким образом, следуют различным правилам. Классы-значения (например, std::pair, std::vector) моделируют встроенные типы. Эти классы обладают следующими свойствами. • Имеют открытые деструктор, копирующий конструктор и присваивание с семантикой значения. • Не имеют виртуальных функций (включая деструктор). • Предназначены для использования в качестве конкретных классов, но не в качестве базовых (см. рекомендацию 35). • Обычно размещаются в стеке или являются непосредственными членами другого класса. Базовые классы представляют собой строительные блоки иерархии классов. Базовый класс обладает следующими свойствами. • Имеет деструктор, который является либо открытым и виртуальным, либо защищенным и невиртуальным (см. рекомендацию 50), а также копирующий конструктор и оператор присваивания, не являющиеся открытыми (см. рекомендацию 53). • Определяет интерфейс посредством виртуальных функций. • Обычно объекты такого класса создаются динамически в куче как часть объекта производного класса и используются посредством (интеллектуальных) указателей. Говоря упрощенно, классы свойств представляют собой шаблоны, которые несут информацию о типах. Класс свойств обладает следующими характеристиками. • Содержит только операторы typedef и статические функции. Класс не имеет модифицируемого состояния или виртуальных функций. • Обычно объекты данного класса не создаются (конструкторы могут быть заблокированы). Классы стратегий (обычно шаблоны) являются фрагментами сменного поведения. Классы стратегий обладают следующими свойствами. • Могут иметь состояния и виртуальные функции, но могут и не иметь их. • Обычно объекты данного класса не создаются, и он выступает в качестве базового класса или члена другого класса. Классы исключений представляют собой необычную смесь семантики значений и ссылок. При генерации исключений они передаются по значению, но должны перехватываться по ссылке (см. рекомендацию 73). Классы исключений обладают следующими свойствами. • Имеют открытый деструктор и конструкторы, не генерирующие исключений (в особенности копирующий конструктор, генерация исключения в котором приводит к завершению работы программы). • Имеют виртуальные функции и часто реализуют клонирование (см. рекомендацию 54). • Предпочтительно делать их производными от std::exception. Вспомогательные классы обычно поддерживают отдельные идиомы (например, RAII — см. рекомендацию 13). Важно, чтобы их корректное использование не было сопряжено с какими-либо трудностями и наоборот — чтобы применять их некорректно было очень трудно (например, см. рекомендацию 53).
Ссылки
[Abrahams01b] • [Alexandrescu00a] • [Alexandrescu00b] • [Alexandrescu01] §3 • [Meyers96] §13 • [Stroustrup00] §8.3.2, §10.3, §14.4.6, §25.1 • [Vandevoorde03] §15
Популярное: Как распознать напряжение: Говоря о мышечном напряжении, мы в первую очередь имеем в виду мускулы, прикрепленные к костям ... Генезис конфликтологии как науки в древней Греции: Для уяснения предыстории конфликтологии существенное значение имеет обращение к античной... Почему двоичная система счисления так распространена?: Каждая цифра должна быть как-то представлена на физическом носителе... Почему люди поддаются рекламе?: Только не надо искать ответы в качестве или количестве рекламы... ©2015-2024 megaobuchalka.ru Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. (442)
|
Почему 1285321 студент выбрали МегаОбучалку... Система поиска информации Мобильная версия сайта Удобная навигация Нет шокирующей рекламы |