C++ C++ C# C# ASP.NET Security ASP.NET Security ASM ASM Скачать Скачать Поиск Поиск Хостинг Хостинг  
  Программа для работы с LPT портом...
Язык: .NET — ©Alexey...
  "ASP.NET Atlas" – AJAX в исполнении Micro...
Язык: .NET — ©legigor@mail.ru...
  "Невытесняющая" Многопоточность...
Язык: C/C++ — ©...
  01.05.2010 — Update World C++: Сборник GPL QT исходников
  15.12.2007 — Весь сайт целиком можно загрузить по ссылкам из раздела Скачать
Хостинг:
Windows 2003, ASP.NET 2.0
бесплатный и от 80 руб./мес


   Отправить письмо
Кулабухов Артем, Беларусь




 Краткий FAQ по C++ 1 / Краткий FAQ по C++ / C++

Краткий FAQ по C++ 1

To the complete English version of this C++ FAQ Lite

К оглавлению

[Этот документ является переводом сборника ответов на часто задаваемые вопросы группы новостей comp.lang.c++. Разделы будут добавляться по мере их перевода. Последнюю версию перевода можно найти по адресу http://quirks.chat.ru/cpp/faq. Ваши замечания и дополнения вы можете высылать мне по адресу: slava_mironov@mail.ru. Последнюю английскую версию этого сборника можно найти на http://www.parashift.com/c++-faq-lite.]

[Авторские права: Вы можете свободно использовать этот документ в некоммерческих целях и делать копии его или его частей для личного использования, при условии сохранения всей информации об авторских правах, включая это предупреждение. Не допускается цитирование этого документа без ссылки на источник. Для коммерческого использования этого документа требуется получить разрешение, во-первых, автора английской версии, во-вторых, автора этого перевода. Автором английской версии является Marshall Cline (cline@parashift.com), автором перевода - Ярослав Миронов (slava_mironov@mail.ru, tada@mail.wplus.net).]

[Все зарегистрированные торговые марки, упоминающиеся в настоящем документе, являются собственностью своих владельцев.]

[В случае, если какой-либо термин допускает неоднозначный перевод на русский язык, я старался передать его в таком виде, как это сделано в русской версии третьего издания книги Бьерна Страуструпа "Язык программирования С++" (BINOM Publishers/Невский Диалект, 1999), чтобы избежать возможной путаницы. Часто в таких случаях перевод термина сопровождается его английским оригиналом в скобках. - YM]

 

  • РАЗДЕЛ [8]: Ссылки

    [8.1] Что такое ссылка?

    [8.2] Что происходит в результате присваивания ссылке?

    [8.3] Что происходит, когда я возвращаю из функции ссылку?

    [8.4] Как можно переустановить ссылку, чтобы она ссылалась на другой объект?

    [8.5] В каких случаях мне стоит использовать ссылки, и в каких - указатели?

    РАЗДЕЛ [9]: Встроенные (inline) функции

    [9.1] Что такое встроенная функция?

    [9.2] Как встроенные функции могут влиять на соотношение безопасности и скорости?

    [9.3] Зачем мне использовать встроенные функции? Почему не использовать просто #define макросы?

    [9.4] Что сделать, чтобы определить функцию - не член класса как встроенную?

    [9.5] Как сделать встроенной функцию - член класса?

    [9.6] Есть ли другой способ определить встроенную функцию - член класса?

    [9.7] Обязательно ли встроенные функции приведут к увеличению производительности?

    РАЗДЕЛ [10]: Конструкторы

    [10.1] Что такое конструкторы?

    [10.2] Есть ли разница между объявлениями List x; и List x();?

    [10.3] Как из одного конструктора вызвать другой конструктор для инициализации этого объекта?

    [10.4] Всегда ли конструктор по умолчанию для Fred выглядит как Fred::Fred()?

    [10.5] Какой конструктор будет вызван, если я создаю массив объектов типа Fred?

    [10.6] Должны ли мои конструкторы использовать "списки инициализации" или "присваивания значений"?

    [10.7] Можно ли пользоваться указателем this в конструкторе?

    [10.8] Что такое "именованный конструктор" ("Named Constructor Idiom")?

    [10.9] Почему я не могу проинициализировать статический член класса в списке инициализации конструктора?

    [10.10] Почему классы со статическими данными получают ошибки при компоновке?

    [10.11] Что такое ошибка в порядке статической инициализации ("static initialization order fiasco")?

    [10.12] Как предотвратить ошибку в порядке статической инициализации?

    [10.13] Как бороться с ошибками порядка статической инициализации объектов - членов класса?

    [10.14] Как мне обработать ошибку, которая произошла в конструкторе?

    РАЗДЕЛ [11]: Деструкторы

    [11.1] Что такое деструктор?

    [11.2] В каком порядке вызываются деструкторы для локальных объектов?

    [11.3] В каком порядке вызываются деструкторы для массивов объектов?

    [11.4] Могу ли я перегрузить деструктор для своего класса?

    [11.5] Могу ли я явно вызвать деструктор для локальной переменной?

    [11.6] А что если я хочу, чтобы локальная переменная "умерла" раньше закрывающей фигурной скобки? Могу ли я при крайней необходимости вызвать деструктор для локальной переменной?

    [11.7] Хорошо, я не буду явно вызывать деструктор. Но как мне справиться с этой проблемой?

    [11.8] А что делать, если я не могу поместить переменную в отдельный блок?

    [11.9] А могу ли я явно вызывать деструктор для объекта, созданного при помощи new?

    [11.10] Что такое "синтаксис размещения" new ("placement new") и зачем он нужен?

    [11.11] Когда я пишу деструктор, должен ли я явно вызывать деструкторы для объектов-членов моего класса?

    [11.12] Когда я пишу деструктор производного класса, нужно ли мне явно вызывать деструктор предка?

  •  

    РАЗДЕЛ [8]: Ссылки

    [8.1] Что такое ссылка?

    Ссылка - это псевдоним (другое имя) для объекта.

    Ссылки часто используются для передачи параметра по ссылке:

        void swap(int& i, int& j)
        {
            int tmp = i;
            i = j;
            j = tmp;
        }
    
        int main()
        {
            int x, y;
            // ...
            swap(x,y);
        }

    В этом примере i и j - псевдонимы для переменных x и y функции main. Другими словами, i - это x. Не указатель на x и не копия x, а сам x. Все, что вы делаете с i, проделывается с x, и наоборот.

    Вот таким образом вы как программист должны воспринимать ссылки. Теперь, рискуя дать вам неверное представление, несколько слов о том, каков механизм работы ссылок. В основе ссылки i на объект x - лежит, как правило, просто машинный адрес объекта x. Но когда вы пишете i++, компилятор генерирует код, который инкрементирует x. В частности, сам адрес, который компилятор использует, чтобы найти x, остается неизменным. Программист на С может думать об этом, как если бы использовалась передача параметра по указателю, в духе языка С, но, во-первых, & (взятие адреса) было бы перемещено из вызывающей функции в вызываемую, и, во-вторых, в вызываемой функции были бы убраны * (разыменование). Другими словами, программист на С может думать об i как о макроопределении для (*p), где p - это указатель на x (т.е., компилятор автоматически разыменовывает подлежащий указатель: i++ заменяется на (*p)++, а i = 7 на *p = 7).

    Важное замечание: несмотря на то что в качестве ссылки в окончательном машинном коде часто используется адрес, не думайте о ссылке просто как о забавно выглядящем указателе на объект. Ссылка - это объект. Это не указатель на объект и не копия объекта. Это сам объект.

     

    [8.2] Что происходит в результате присваивания ссылке?

    Вы меняете состояние ссыльного объекта (того, на который ссылается ссылка).

    Помните: ссылка - это сам объект, поэтому, изменяя ссылку, вы меняете состояние объекта, на который она ссылается. На языке производителей компиляторов ссылка - это lvalue (left value - значение, которое может появиться слева от оператора присваивания).

     

    [8.3] Что происходит, когда я возвращаю из функции ссылку?

    В этом случае вызов функции может оказаться с левой стороны оператора (операции) присваивания.

    На первый взгляд, такая запись может показаться странной. Например, запись f() = 7 выглядит бессмысленной. Однако, если - это объект класса Array, для большинства людей запись a[i] = 7 является осмысленной, хотя a[i] - это всего лишь замаскированный вызов функции Array::operator[](int), которая является оператором обращения по индексу для класса Array:

        class Array {
        public:
            int size() const;
            float& operator[] (int index);
            // ...
        };
    
        int main()
        {
            Array a;
            for (int i = 0; i < a.size(); ++i)
                a[i] = 7; // В этой строке вызывается Array::operator[](int)
        }

     

    [8.4] Как можно переустановить ссылку, чтобы она ссылалась на другой объект?

    Невозможно в принципе.

    Невозможно отделить ссылку от ее объекта.

    В отличие от указателя, ссылка, как только она привязана к объекту, не может быть "перенаправлена" на другой объект. Ссылка сама по себе ничего не представляет, у нее нет имени, она сама - это другое имя для объекта. Взятие адреса ссылки дает адрес объекта, на который она ссылается. Помните: ссылка - это объект, на который она ссылается.

    С этой точки зрения, ссылка похожа на const указатель [18.5], такой как int* const p (в отличие от указателя на const [18.4], такого как const int* p). Несмотря на большую схожесть, не путайте ссылки с указателями - это не одно и то же.

     

    [8.5] В каких случаях мне стоит использовать ссылки, и в каких - указатели?

    Используйте ссылки, когда можете, а указатели - когда это необходимо.

    Ссылки обычно предпочтительней указателей, когда вам ненужно их "перенаправлять" [8.4]. Это обычно означает, что ссылки особенно полезны в открытой (public) части класса. Ссылки обычно появляются на поверхности объекта, а указатели спрятаны внутри.

    Исключением является тот случай, когда параметр или возвращаемый из функции объект требует выделения "охранного" значения для особых случаев. Это обычно реализуется путем взятия/возвращения указателя, и обозначением особого случая при помощи передачи нулевого указателя (NULL). Ссылка же не может ссылаться на разыменованный нулевой указатель.

    Примечание: программисты с опытом работы на С часто недолюбливают ссылки, из-за того что передача параметра по ссылке явно никак не обозначается в вызывающем коде. Однако с обретением некоторого опыта работы на С++, они осознают, что это одна из форм сокрытия информации, которая является скорее преимуществом, чем недостатком. Т.е., программисту следует писать код в терминах задачи, а не компьютера (programmers should write code in the language of the problem rather than the language of the machine).

     

    РАЗДЕЛ [9]: Встроенные (inline) функции

    [9.1] Что такое встроенная функция?

    Встроенная функция - это функция, код которой прямо вставляется в том месте, где она вызвана. Как и макросы, определенные через #define, встроенные функции улучшают производительность за счет стоимости вызова и (особенно!) за счет возможности дополнительной оптимизации ("процедурная интеграция").

     

    [9.2] Как встроенные функции могут влиять на соотношение безопасности и скорости?

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

    К сожалению, этот метод идет вразрез с безопасностью типов, а также требует вызова функции для доступа к любым полям структуры (если вы позволили бы прямой доступ, то его мог бы получить кто угодно, поскольку будет известно, как интерпретировать данные, на которые указывает void*. Такое поведение со стороны пользователя приведет к сложностям при последующем изменении структуры подлежащих данных).

    Стоимость вызова функции невелика, но дает некоторую прибавку. Классы С++ позволяют встраивание функций, что дает вам безопасность инкапсуляции вместе со скоростью прямого доступа. Более того, типы параметры встраиваемых функций проверяются компилятором, что является преимуществом по сравнению с (?)сишными #define макросами.

     

    [9.3] Зачем мне использовать встроенные функции? Почему не использовать просто #define макросы?

    Поскольку #define макросы опасны [9.3], опасны [34.1], опасны [34.2], опасны [34.3].

    В отличие от #define макросов, встроенные (inline) функции неподвержены известным ошибкам двойного вычисления, поскольку каждый аргумент встроенной функции вычисляется только один раз. Другими словами, вызов встроенной функции - это то же самое что и вызов обычной функции, только быстрее:

    	// Макрос, возвращающий модуль (абсолютное значение) i
    	#define unsafe(i) \
    		( (i) >= 0 ? (i) : -(i) )
    
    	// Встроенная функция, возвращающая абсолютное значение i
    	inline
    	int safe(int i)
    	{
    		return i >= 0 ? i : -i;
    	}
    
    	int f();
    
    	void userCode(int x)
    	{
    		int ans;
    		ans = unsafe(x++); // Ошибка! x инкрементируется дважды
    		ans = unsafe(f()); // Опасно! f() вызывается дважды
    		ans = safe(x++); // Верно! x инкрементируется один раз
    		ans = safe(f()); // Верно! f() вызывается один раз
    	}

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

    Макросы вредны для здоровья; не используйте их, если это не необходимо