C++: что происходит, если вернуть из функции ссылку?
На самом деле, реально происходит одно - вызов функции может оказаться слева от оператора присваивания.
На первый взгляд, звучит странно, ведь к записям вроде
a(1)=1;
мы не привыкли :) Однако, если a
- объект класса, представляющего собой реализацию массива, в записи
a[1]=1;
мы не увидим абсолютно ничего странного, хотя a[1]
будет всего лишь "замаскированным" вызовом функции a.operator [](int)
, которая является опреатором обращения по индексу для этого класса.
Поясним сказанное на примере. Создадим консольное приложение без предварительно откомпилированного заголовка, например, в Visual Studio (C++). Добавив к проекту класс MyArray
, реализуем простой пример класса, например, такой, только немного перепишем код с адаптацией под более современные стандарты:
Файл MyArray.h
#pragma once #include <iostream> #include <float.h> class MyArray { private: int low,hi; float *value; public: MyArray() : low(0), hi(0), value(NULL) { } MyArray(int); MyArray(int,int); ~MyArray(); float & operator [] (int); inline int first(void) { return low; } inline int last(void) { return hi; } inline int size (void) { return (hi-low+1); } void show (void); };
Файл MyArray.cpp
#include "MyArray.h" MyArray::MyArray (int n) { new MyArray (0,n-1); } MyArray::MyArray (int k1, int k2) { low=k1; hi=k2; value = new float [size()]; } MyArray::~MyArray () { delete value; } float & MyArray::operator [] (int k) { if (k<low || k>hi) { float *NaN; *NaN = FLT_MAX; return *NaN; } return value[k-low]; } void MyArray::show (void ) { std::cout << std::endl<< size() << " item(s): "; for (int i=first(); i<=last(); i++) std::cout << value[i-low] << " "; std::cin.sync(); std::cin.get(); }
В главной программе остаётся подключить MyArray.h
#include "MyArray.h"
и создать хотя бы один экземпляр класса MyArray
:
MyArray a (1,10); for (int i=a.first(); i<=a.last(); i++) a[i]=(float)i; //!!! a.show();
Замените строчку, помеченную комментарием //!!!
на
a.operator [](i)=(float)i;
и получите явный вызов функции слева от оператора присваивания.
В качестве резюме
В результате присваивания чего-то ссылке мы меняем сам "ссыльный" объект, ссылка и есть сам объект, изменяя её, мы меняем и состояние объекта, на который она ссылается. Не зря на языке производителей компиляторов ссылка называется lvalue (left value - значение, которое может появляться слева от оператора присваивания).
Ссылку в принципе нельзя переустановить так, чтобы она ссылалась на другой объект. В отличие от указателя, ссылка, как только она привязана к объекту, не может быть "переставлена" на другой объект. Ссылка "сама по себе" ничего не представляет, у неё нет имени, она сама - словно "другое имя" для объекта. Взятие адреса ссылки даёт адрес объекта , на который она ссылается.
С этой точки зрения ссылка похожа на const указатель, такой как int * const p
(в отличие от указателя на const
, такого как const int *p
). Ссылка - это объект, на который она ссылается. Не путайте с указателями и используйте ссылки всегда, когда можете, а указатели - только тогда, когда без них не обойтись :)
Суть всех последних изменений в компиляторах и смысл существования всех Java-подобных языков/сред - именно "закрыть" классические нативные сишные указатели, как основной фактор уязвимости кода (и свободы его написания тоже :)
Ну а конкретно ухищряться можно по-разному - понаделать обёрток вроде ^
и gcnew
из C++ CLI или вовсе упразднить указатели как в "чистом" Java.
22.05.2014, 17:51 [11786 просмотров]