Рейтинговые книги
Читем онлайн Освой самостоятельно С++ за 21 день. - Джесс Либерти

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 52 53 54 55 56 57 58 59 60 ... 170

Листинг 3.12. Передача ссылок на объекты

1: // Листинг 9.12.

2: // Передача ссылок на объекты

3:

4: #include <iostream.h>

5:

6: class SimpleCat

7: {

8:    public:

9:       SimpleCat();

10:      SimpleCat(SimpleCat&);

11:      ~SimpleCat();

12:

13:   int GetAge() const { return itsAge; }

14:   void SetAge(int age) { itsAge = age: }

15:

16:   private:

17:      int itsAge;

18: };

19:

20: SimpleCat::SimpleCat()

21: {

22:    cout << "Simple Cat Constructor...n";

23:    itsAge = 1;

24: }

25:

26: SimpleCat::SimpleCat(SimploCat&)

27: {

28:    cout << "Simple Cat Copy Cunstructor...n";

29: }

30:

31: SimpleCat::~SimpleCat()

32: {

33:    cout << "Simple Cat Destructor...n";

34: }

35:

36: const SimpleCat & FunctionTwo (const SimpleCat & theCat);

37:

38: int main()

39: {

40:    cout << "Making a cat...n";

41:    SimpleCat Frisky;

42:    cout << "Frisky is " << Frisky.GetAge() << " years oldn";

43:    int age = 5;

44:    Frisky,SetAge(age);

45:    cout << "Frisky is " << Frisky.GetAge() << " years oldn";

46:    cout << "Calling FunctionTwo...n";

47:    FunctionTwo(Frisky);

48:    cout << "Frisky is " << Frisky.GetAge() << " years oldn";

49:    return 0;

50: }

51:

52: // functionTwo, passes a ref to a const object

53: const SimpleCat & FunctionTwo (const SimpleCat & theCat)

54: {

55:    cout << "Function Two. Returning...n";

56:    cout << "Frisky is now " << theCat.GetAge();

57:    cout << " years old n";

58:    // theCat.SetAge(8); const!

59:    return theCat;

60: }

Результат:

Making a cat...

Simple Cat constructor...

Frisky is 1 years old

Frisky is 5 years old

Calling FunctionTwo...

FunctionTwo. Returning...

Frisky is now 5 years old

Frisky is 5 years old

Simple Cat Destructor...

Анализ: Результат работы этой программы идентичен результату, показанному после листинга9.11. Единственное существенное изменение — функция FunctionTwo() теперь принимает и возвращает ссылки на константный объект. И вновь-таки работа со ссылками несколько проще, чем работа с указателями, хотя при этом достигается та же экономия средств и эффективность выполнения, а также обеспечивается надежность за счет использования спецификатора const.

Константные ссылки

Программисты, работающие с языком C++, обычно не видят разницы между константной ссылкой на объект SimpleCat и ссылкой на константный объект SimpleCat. Сами ссылки нельзя переназначать, чтобы они ссылались на другой объект, поэтому они всегда константны. Если к ссылке применено ключевое слово const, то это делает константным объект, с которым связана ссылка.

Когда лучше использовать ссылки, а когда - указатели

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

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

В качестве примера рассмотрим оператор new. Если оператор new не сможет выделить память для нового объекта, он возвратит нулевой указатель. А поскольку ссылка не может быть нулевой, вы не должны инициализировать ссылку на эту память до тех пор, пока не проверите, что она не нулевая. В следующем примере показано, как это сделать:

int *pInt = new int;

if (pInt != NULL)

int &rInt = *pInt;

В этом примере объявляется указатель pInt на значение типа int, который инициализируется областью памяти, возвращаемой оператором new. Адрес этой области памяти (в указателе pInt) тестируется, и, если он не равен значению null, указатель pInt разыменовывается. Результат разыменования переменной типа int представляет собой объект типа int, и ссылка rInt инициализируется этим объектом. Следовательно, ссылка rInt становится псевдонимом для переменной типа int, возвращаемой оператором new.

Рекомендуется:Передавайте функциям параметры как ссылке везде, где это возможно. Обеспечивайте возврат значений как ссылок везде, где это возможно. Используйте спецификатор const для защиты ссылок и указателей везде, где это возможно.

Не рекомендуется: Не используйте указатели, если вместо них можно использовать ссылки. Не возвращайте ссылки на локальные объекты.

Коктейль из ссылок и указателей

Не будет ошибкой в списке параметров одной функции объявить как указатели, так и ссылки, а также объекты, передаваемые как значения, например:

CAT * SomeFunction (Person &theOwner, House *theHouse, int age);

Это объявление означает, что функция SomeFunction принимает три параметра. Первый является ссылкой на объект типа Person, второй — указателем на объект типа House, а третий — целочисленным значением. Сама же функция возвращает указатель на объект класса CAT.

Следует также отметить, что при объявлении соответствующих переменных можно использовать разные стили размещения операторов ссылки (&) и косвенного обращения (*). Вполне законной будет любая из следующих записей:

1: CAT& rFrisky;

2: CAT & rFrisky;

3: CAT &rFrisky;

Примечание:Символы пробелов в программах на языке C++ полностью игнорируются, поэтому везде,  де вы видите пробел, можно ставить несколько пробелов, символов табуляции или символов разрывов строк. Оставив в покое вопросы свободного волеизъявления, попробуем разобраться в том, какой вариант все же лучше других. Как ни странно, можно найти аргументы в защиту каждого из трех вариантов. Аргумент в защиту первого варианта состоит в следующем. rFrisky — это переменная с именем rFrisky, тип которой можно определить как ссылку на объект класса CAT. Поэтому вполне логично, чтобы оператор & стоял рядом с типом.

Однако есть и контраргумент. CAT — это тип. Оператор & является частью объявления, которое включает имя переменной и амперсант. Но следует отметить, что слияние вместе символа & и имени типа CAT может привести к возникновению следующей ошибки:

CAT& rFrisky, rBoots;

Поверхностный анализ этой строки может натолкнуть на мысль, что как переменная rFrisky, так и переменная rBoots являются ссылками на объекты класса CAT. Однако это не так. На самом деле это объявление означает, что rFrisky является ссылкой на объект класса CAT, а rBoots (несмотря на свое имя с характерным префиксом) — не ссылка, а обыкновенная переменная типа CAT. Поэтому последнее объявление следует переписать по-другому:

CAT &rFrisky, rBoots;

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

CAT& rFrisky; CAT Boots;

Примечание:Наконец, многие программисты не обращают внимания на приведенные аргументы и, считая, что истина находится посередине, выбирают средний вариант (средний, кстати, в двух смыслах), который иллюстрируется случаем 2:

2: CAT & rFrisky;

Безусловно, все сказанное до сих пор об операторе ссылки (&) относится в равной степени и к оператору косвенного обращения (<<). Выберите стиль, который вам подходит, и придерживайтесь его на протяжении всей программы, ведь ясность текста программы — одна из основных составляющих успеха.

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

1. Размещать амперсант или звездочку посередине, окаймляя этот символ пробелами с двух сторон.

2. Никогда не объявлять ссылки, указатели и переменные в одной и той же строке программы.

Не возвращайте ссылку на объект, который находиться вне области видимости!

Научившись передавать аргументы как ссылки на объекты, программисты порой теряют чувство реальности. Не стоит забывать, что все хорошо в меру. Помните, что ссылка всегда служит псевдонимом некоторого объекта. При передаче ссылки в функцию или из нее не забудьте задать себе вопрос: "Что представляет собой объект, псевдонимом которого я манипулирую, и будет ли он существовать в момент его использования?"

В листинге 9.13 показан пример возможной ошибки, когда функция возвращает ссылку на объект, которого уже не существует.

Листинг 9.13. Возвращение ссылки на несуществующий объект

1: // Листинг 9.13.

2: // Возвращение ссылки на объект,

3: // которого больше не существует

4:

5: #include <iostream.h>

6:

7: class SimpleCat

8: {

1 ... 52 53 54 55 56 57 58 59 60 ... 170
На этой странице вы можете бесплатно читать книгу Освой самостоятельно С++ за 21 день. - Джесс Либерти бесплатно.

Оставить комментарий