БлогNot. C++: что происходит, если вернуть из функции ссылку?

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 [11683 просмотра]


теги: c++ программирование ссылки studio

К этой статье пока нет комментариев, Ваш будет первым