Назад | Начало урока | Вперед
Содержание

Глава 8

Вверх

Ссылки в качестве альтернативы.

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

Это подтверждается следующей программой.

Передача ссылок объектам.

//Listing 9.11 // Passing pointers to objects


#include <iostream>
using namespace std;

class SimpleCat {

public:
SimpleCat();
SimpleCat(SimpleCat&);
~SimpleCat();

int GetAge() const {

return itsAge;
}
void SetAge(int age) {
itsAge = age;
}

private:
int itsAge;

};

SimpleCat::SimpleCat() {


cout << "Simple Cat Constructor...\n";
itsAge = 1;
}

SimpleCat::SimpleCat(SimpleCat&) {


cout << "Simple Cat Copy Constructor...\n";
}

SimpleCat::~SimpleCat() {


cout << "Simple Cat Destructor...\n";
}

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

int main() {


cout << "Making a cat...\n";
SimpleCat Frisky;

cout << "Frisky is " ;
cout << Frisky.GetAge();
cout << " years old\n";

int age = 5;
Frisky.SetAge(age);

cout << "Frisky is " ;
cout << Frisky.GetAge();
cout << " years old\n";

cout << "Calling FunctionTwo...\n";
FunctionTwo(Frisky);

cout << "Frisky is " ;
cout << Frisky.GetAge();
cout << " years old\n"; return 0;

}

// functionTwo, передает ссылку постоянному объекту

const SimpleCat & const FunctionTwo
(const SimpleCat & const theCat) {


cout << "Function Two. Returning...\n";
cout << "Frisky is now " << theCat.GetAge();
cout << " years old \n";
// theCat.SetAge(8); const!
return theCat;
}

Результат:

1.Making a cat...
2.Simple Cat Constructor...
3.Frisky is 1 years old
4.Frisky is 5 years old

5.Calling FunctionTwo...
6.Function Two. Returning...
7.Frisky is now 5 years old
8.Frisky is 5 years old
9.Simple Cat Destructor...

Анализ:

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


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

Вверх

Когда использовать ссылки а когда указатели.

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

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

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

int *pInt=new int;
if(pInt!=NULL)
int &rInt=*pInt;

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


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

Не используйте указатели если вместо них можно использовать ссылки.

Вверх

Совместное использование ссылок и указателей

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

-.-.-.-.-.-.-.-.-.-.

Вверх

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

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

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

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

// Listing 9.13 // Returning a reference to an object // which no longer exists


#include <iostream>

class SimpleCat {

public:
SimpleCat (int age, int weight);
~SimpleCat() {
}
int GetAge() {
return itsAge;
}
int GetWeight() {
return itsWeight;
} private:
int itsAge;
int itsWeight;
};

SimpleCat::SimpleCat(int age, int weight) {


itsAge = age;
itsWeight = weight;
}

SimpleCat &TheFunction();

int main() {


SimpleCat &rCat = TheFunction();
int age = rCat.GetAge();
std::cout << "rCat is " << age << " years old!\n";
return 0;
}

SimpleCat &TheFunction() {


SimpleCat Frisky(5,9);
return Frisky;
}

Результат:

Программа не компилируется!


На компиляторе фирмы Borland программа не компилируется, но для нее подходят компиляторы Microsoft. Однако профессиональный программист никогда не станет полагаться на уступки компилятора
Анализ:

В стр 7-17 объявляется класс SimpleCat. В стр 29 инициализируется ссылка на объект класса SimpleCat

29 SimpleCat &rCat = TheFunction();

с использованием результатов вызова функции theFunction(), объявленной в стр 25.

Согласно объявлению функция возвращает ссылку на объект класса SimpleCat. SimpleCat &TheFunction()

В теле функции theFunction() объявляется локальный объект типа SimpleCat и инициализируется значение его возраста и веса.Затем этот объект возвращается по ссылке.

SimpleCat &TheFunction() {

SimpleCat Frisky(5,9);
return Frisky;
}

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

Если объект создается внутри функции, как в данном примере, а затем возвращаемым значением функции является ссылка на этот объект, то когда функция заканчивает свою работу, созданный в ней объект уничтожается и на что спрашивается теперь указывает ссылка?На несуществующий объект!? А это уж нонсенс!


Назад | Начало урока | Вверх | Вперед
Содержание

Hosted by uCoz