Почему C/C++ не “#pragma однажды” стандарт ISO?

От этого статья MSDN:

существуют немного служебные связаны с созданием объекта StringBuilder, и вовремя и память. На машине с быстродействующей памятью StringBuilder становится стоящим при выполнении приблизительно пяти операций. Как показывает опыт, я сказал бы, что 10 или больше строковых операций являются выравниванием для издержек на любой машине, даже более медленная.

Поэтому, если Вы доверяете, MSDN идет с StringBuilder, если необходимо сделать больше чем 10 строковых операций/конкатенаций - в других отношениях простая строка concat с '+' прекрасна.

49
задан SamB 6 August 2011 в 07:50
поделиться

7 ответов

Директива вроде #pragma once нетривиально определить полностью переносимым способом, который имеет очевидные преимущества. Некоторые концепции, вызывающие вопросы, не определены должным образом во всех системах, поддерживающих C , и его простое определение может не принести никакого преимущества по сравнению с обычными средствами защиты включения.

Когда компиляция встречает #pragma once , как ему идентифицировать этот файл, чтобы он больше не включал его содержимое?

Очевидный ответ - уникальное местоположение файла в системе. Это нормально, если в системе есть уникальные местоположения для всех файлов, но многие системы предоставляют ссылки (символические ссылки и жесткие ссылки), что означает, что «файл» не имеет уникального местоположения. Следует ли повторно включить файл только потому, что он был найден под другим именем? Вероятно, нет.

Но теперь возникает проблема: как можно определить поведение #pragma once таким образом, чтобы оно имело точное значение на всех платформах - даже на тех, которые даже не иметь каталоги, не говоря уже о символических ссылках - и при этом добиться желаемого поведения в системах, в которых они есть?

Можно сказать, что идентичность файла определяется его содержимым, поэтому, если во включенном файле есть #pragma once и файл, который имеет точно такое же содержимое, тогда второй и последующие #include не будут иметь никакого эффекта.

Это легко определить и имеет четко определенную семантику. У него также есть хорошие свойства, так что если проект перемещается из системы, которая поддерживает и использует ссылки файловой системы на ту, которая не поддерживает, он все равно ведет себя так же.

С другой стороны, каждый раз, когда встречается включаемый файл, содержащий #pragma once его содержимое должно быть проверено на соответствие всем остальным файлам с помощью #pragma once , который уже был включен. Это означает снижение производительности, подобное использованию #include охранников в любом случае, и добавляет немалую нагрузку на авторов компилятора. Очевидно, что результаты этого можно кэшировать, но то же самое верно и для обычных средств защиты включения.

49
ответ дан 7 November 2019 в 11:35
поделиться

Включить охранники определенно раздражает, и C изначально должен был быть разработан таким образом, чтобы заголовки по умолчанию включались один раз, что требовало специальной опции для включения заголовка несколько раз.

Однако это не так, и вы в основном застряли с необходимостью использовать включают охранников. Тем не менее, #pragma once довольно широко поддерживается, поэтому вы можете обойтись без него.

Лично я Я решаю вашу 1-ю проблему (включающие файлы с одинаковыми названиями), добавляя GUID в include guard. Это уродливо, и большинство людей ненавидят его (поэтому я часто вынужден не использовать его на работе), но ваш опыт показывает, что идея имеет некоторую ценность - даже если она ужасно уродлива (но опять же, вся вещь include guard - своего рода хакерство - почему бы не пойти ва-банк?):

#ifndef C_ASSERT_H_3803b949_b422_4377_8713_ce606f29d546
#define C_ASSERT_H_3803b949_b422_4377_8713_ce606f29d546

// blah blah blah...

#endif

Я слышал, что компиляторы на самом деле не открывают повторно файлы заголовков, в которых есть защита (они научились распознавать идиому). Я не уверен, правда ли это (или насколько это правда); Никогда не измерял. Я тоже не переживаю по этому поводу, но мои проекты не настолько огромны, чтобы это было проблемой.

Мой хак GUID в значительной степени решает пункты 1, 5 и 6. Я просто живу с пунктами 2, 3 и 4 . Фактически, для пункта 2 вы можете жить без переименования макроса include guard при переименовании файла, поскольку GUID гарантирует, что он останется уникальным. На самом деле, нет никаких причин включать имя файла в GUID. Но я верю - полагаю, традиция.

19
ответ дан 7 November 2019 в 11:35
поделиться
  1. Как часто вам нужно добавлять включаемый файл в этот проект? Неужели так сложно добавить DIRNAME_FILENAME в охрану? И всегда есть GUID.
  2. Вы действительно так часто переименовываете файлы? Когда-либо? Кроме того, размещение GUARD в #endif так же раздражает, как и любой другой бесполезный комментарий.
  3. Я сомневаюсь, что ваши определения защиты 1000 файлов заголовков составляют даже небольшой процент от количества определений, сгенерированных вашими системными библиотеками (особенно в Windows) .
  4. Я думаю, что MSC 10 для DOS (20+ лет назад) отслеживал, какие заголовки были включены, и если бы они содержали охрану, пропустили бы их, если бы снова включили. Это старая технология.
  5. пространства имен и шаблоны не должны охватывать заголовки. Дорогой, не говорите мне, что вы делаете это:

     template 
    class bar {
    #include "bar_impl.h"
    };
    
  6. Вы уже это сказали.

14
ответ дан 7 November 2019 в 11:35
поделиться

Проблема возникает, когда у вас есть заголовки с одинаковым именем файла в разных каталогах.

Итак, у вас есть два заголовка, которые в вашем проекте называются ice_cream_maker.h , в обоих из которых определен класс с именем ice_cream_maker , который выполняет ту же функцию? Или вы вызываете каждый класс в своей системе foo ?

Тем не менее, определение включается один раз, компилятор по-прежнему открывает заголовок каждый раз, когда встречается включение заголовка.

Отредактируйте код, чтобы не включать заголовки несколько раз.

Для зависимых заголовков (а не основного заголовка для библиотеки) я часто использую такие средства защиты заголовков:

#ifdef FOO_BAR_BAZ_H
#error foo_bar_baz.h multiply included
#else
#define FOO_BAR_BAZ_H

// header body

#endif
4
ответ дан 7 November 2019 в 11:35
поделиться

IIRC, #pragma ничего не является частью язык. И на практике это проявляется очень часто.

(править: полностью согласен с тем, что система включения и связывания должна была стать в центре внимания нового стандарта, поскольку это одна из самых очевидных слабостей в наши дни. и возраст »)

3
ответ дан 7 November 2019 в 11:35
поделиться

Вероятно, вы можете избежать конфликтов имен, не прибегая к случайным строкам, установив защиту включения, которая будет содержать имя класса и пространство имен , которое находится в файле.

Кроме того, , #pragma once поддерживается как компиляторами MS, так и GCC в течение некоторого времени, так почему вас беспокоит то, что он не соответствует стандарту ISO?

2
ответ дан 7 November 2019 в 11:35
поделиться

Прагматическое решение:

1) выберите некоторую согласованную политику присвоения имен (например, путь относительно корня проекта + имя файла или что-то еще по вашему выбору). Включите возможные исключения для стороннего кода.

2) напишите программу (подойдет простой скрипт на Python) для рекурсивного обхода исходного дерева и убедитесь, что все средства защиты соответствуют политике. И когда охранники ошибаются, выведите diff (или сценарий sed, или что-то еще), которое пользователь может легко применить для исправления. Или просто попросите подтверждения и внесите изменения из той же программы.

3) Заставьте всех в проекте использовать ее (скажем, перед отправкой в ​​систему контроля версий).

2
ответ дан 7 November 2019 в 11:35
поделиться