Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Языки программирования.docx
Скачиваний:
3
Добавлен:
09.09.2019
Размер:
64.85 Кб
Скачать

Левые функции

Lvalue (left value)

Rvalue (right value)

X+Y=Z неверно

Z=X+Y верно

W=2Z верно

Const double PI=acos(-1.0);

Double S=2*PI*pow(r,2);

PI=… неверно в связи с тем, что выше указано то, что PI – константа

Int A[10]={0,1,2,3,4,5,6,7,8,9};

A[2]=A[9];

Int Sum(int a,int b)

{Return a+b;}

Int main()

{int s=sum(5,10);

}

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

Int &ff(int k)

{return k;}// неправильная функция, возвращающая ссылку, т.к. она возвращает адрес локальной переменной. Но тем самым при запуске программы мы получим только предупреждение, что мы возвращаем адрес локальной переменной, но позволяет запустить программу.

Int f(int &i)

{return I;}// здесь получается правильное значение 5

Int main()

{

Int n=5;

Cout<<f(ff(n))<<endl;

Int m=2

Cout<<f(g)<<endl;

Return 0;

}

Как решить эту проблему? Существует 2 способа решения данной проблемы:

  1. Передавать параметры по ссылке Int &ff(int &k)

  2. Возвращать ссылку на статическую переменную:

{static int a; a=k; return a;}

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

Здесь были Регинины каракули…

Тривиальный пример:

Левая функция для выбора максимума двух чисел.

Здесь тоже они были…

Double& max(double &x, double &y)

{return (x>y)?x:y;}

{double Z=0.0;

Z=max(10.0,15.0);// обычный вызов

Double a=5, b=6, c=7;

Max(a,b)=10.0; // b=10.0

Max(max(a,b),c)=10 //a=10

Max(max(a,b)=10,c)=0 //b=0.0

//левая функция для минимума массива

Int &Mmin (int d[], int n)

{ int im=0

For (int i=0;i<n;i++)

Im=d[im]<d[i]?im:i;

Return d[im];}

Int main ()

{int x[10]={10,20,40,5,6,7,8,9,50,12}

Int m=Mmin(x,10);

Cout<<”m=”<<m<<endl;

Mmin(x,10)=0; // Минимальный элемент будет заменен на 0

}

Перегрузка операции присваивания.

Если мы не представим свою версию операции присваивания, то компилятор использует свою версию, сгенерированную по умолчанию. Версия по умолчанию работает подобно конструктору копирования по умолчанию. А именно выполняет процесс присваивания (копирования) член за членом. Однако, это не одно и то же.

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

Для примера класса Box операция присваивания работает без проблем. Но для других классов, в которых память для членов выделяется динамически, может возникнуть потенциальная угроза породить хаос в программе.

Box box1 (4.0,2.0,3.0);

Box box2=box1;// операция присваивания по умолчанию сработает адекватно

Message mess1(“The Visual studio 2010”);

Message mess2=mess1;// могут возникнуть проблемы

Эффект от применения операции присваивания по умолчанию для объектов класса типа Message будет такой же, как и от использования конструктора копирования по умолчанию. Возникнут проблемы, поскольку каждый объект имеет указатель на одну и ту же строку. И если строка изменится в одном объекте, то изменится в обоих. Кроме того, если один объект разрушается деструктором при выходе из области видимости, то его деструктор освобождает память, использованную для строки, на которую указывает указатель другого объекта. Но память уже была освобождена и, возможно, уже используется для других целей в другом месте. Поэтому в таких случаях необходимо представить собственную версию перегрузки операции присваивания.

В теле класса Message следует добавить оператор:

Message &operator=(const Message &aMess)

{delete [] pmessage;

Pmessage=new char[strlen(aMess.pmessage)+1];

Strcpy(this->pmessage, aMess.pmessage);

Return *this;}

Почему тип возврата функции operator= должна быть ссылка?

Чтобы ответить на этот вопрос, рассмотрим 2 варианта присваивания.

Допустим, что есть 3 объекта типа message.

Mess3=mess2=mess1;