Почему ссылки не восстанавливаются в C ++ - PullRequest
67 голосов
/ 08 апреля 2009

C ++ ссылки имеют два свойства:

  • Они всегда указывают на один и тот же объект.
  • Они не могут быть 0.

Указатели противоположны:

  • Они могут указывать на разные объекты.
  • Они могут быть 0.

Почему в C ++ нет «ненулевой, перезагружаемой ссылки или указателя»? Я не могу придумать вескую причину, по которой ссылки не должны быть перезаписываемыми.

Edit: Вопрос возникает часто, потому что я обычно использую ссылки, когда хочу убедиться, что «ассоциация» (я избегаю слов «ссылка» или «указатель» здесь) никогда не будет недействительной.

Я не думаю, что когда-либо думал, что "здорово, что эта ссылка всегда относится к одному и тому же объекту". Если бы ссылки были перезагружаемыми, можно было бы получить текущее поведение, подобное этому:

int i = 3;
int& const j = i;

Это уже допустимый C ++, но бессмысленный.

Я повторяю свой вопрос следующим образом: «Что послужило основанием для« ссылки » - это объектный дизайн? Почему считалось полезным иметь ссылки всегда быть тем же объектом, а не только когда объявлен как const? "

Ура, Феликс

Ответы [ 17 ]

0 голосов
/ 04 октября 2016

Есть обходной путь, если вы хотите, чтобы переменная-член была ссылкой, и вы хотите иметь возможность ее перепривязать. Хотя я нахожу это полезным и надежным, заметьте, что он использует некоторые (очень слабые) предположения о расположении памяти. Вам решать, соответствует ли это вашим стандартам кодирования.

#include <iostream>

struct Field_a_t
{
    int& a_;
    Field_a_t(int& a)
        : a_(a) {}
    Field_a_t& operator=(int& a)
    {
        // a_.~int(); // do this if you have a non-trivial destructor
        new(this)Field_a_t(a);
    }
};

struct MyType : Field_a_t
{
    char c_;
    MyType(int& a, char c)
        : Field_a_t(a)
        , c_(c) {}
};

int main()
{
    int i = 1;
    int j = 2;
    MyType x(i, 'x');
    std::cout << x.a_;
    x.a_ = 3;
    std::cout << i;
    ((Field_a_t&)x) = j;
    std::cout << x.a_;
    x.a_ = 4;
    std::cout << j;
}

Это не очень эффективно, так как вам нужен отдельный тип для каждого переназначаемого ссылочного поля и сделать их базовыми классами; также здесь есть слабое предположение, что класс, имеющий единственный ссылочный тип, не будет иметь __vfptr или любого другого type_id связанного поля, которое потенциально может разрушить привязки времени выполнения MyType. Все компиляторы, которых я знаю, удовлетворяют этому условию (и в этом нет никакого смысла).

0 голосов
/ 08 апреля 2009

Я бы предположил, что это связано с оптимизацией.

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

0 голосов
/ 31 июля 2012

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

struct A{
    int y;
    int& x;
     A():y(0),x(y){}
};

int main(){
  A a;
  const A& ar=a;
  ar.x++;
}

работает. Смотри

Расчетные причины поведения эталонных членов классов, передаваемых по константной ссылке

0 голосов
/ 09 апреля 2009

Тот факт, что ссылки в C ++ не являются обнуляемыми, является побочным эффектом того, что они являются просто псевдонимами.

0 голосов
/ 08 апреля 2009

Быть наполовину серьезным: ИМХО, чтобы они немного отличались от указателей;) Вы знаете, что можете написать:

MyClass & c = *new MyClass();

Если бы вы могли позже написать:

c = *new MyClass("other")

имеет ли смысл иметь какие-либо ссылки наряду с указателями?

MyClass * a =  new MyClass();
MyClass & b = *new MyClass();
a =  new MyClass("other");
b = *new MyClass("another");
0 голосов
/ 08 апреля 2009

Мне всегда было интересно, почему они не сделали для этого оператор ссылки (скажем: =).

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

Нет, я не рекомендую повторять мой трюк. Он сломается, если будет перенесен на достаточно другую архитектуру.

0 голосов
/ 08 апреля 2009

Потому что иногда вещи не должны быть переориентированы. (Например, ссылка на синглтон.)

Потому что в функции замечательно знать, что ваш аргумент не может быть нулевым.

Но в основном потому, что он позволяет использовать что-то, что действительно является указателем, но действует как объект локального значения. C ++ старается изо всех сил, цитируя Страуструпа, заставлять экземпляры классов «делать как целые числа d». Передача int через vaue - это дешево, потому что int вписывается в машинный регистр. Классы часто больше чем целые, и передача их по значению имеет значительные накладные расходы.

Возможность передавать указатель (который часто имеет размер типа int или, может быть, два целых), который «похож на» объект-значение, позволяет нам писать более чистый код без «подробностей реализации» разыменований. И, наряду с перегрузкой операторов, это позволяет нам писать классы, использующие синтаксис, подобный синтаксису, используемому с int. В частности, он позволяет нам писать классы шаблонов с синтаксисом, который может быть одинаково применен к примитивам, таким как целые и классы (например, класс комплексных чисел).

И, особенно с перегрузкой операторов, есть места, где мы должны возвращать объект, но опять же, намного дешевле вернуть указатель. Oncve снова, возвращая ссылку, наш "вне".

И указатели сложны. Может быть, не для вас, и не для тех, кто понимает указатель - это просто значение адреса памяти. Но, вспомнив мой класс CS 101, они запутали нескольких учеников.

char* p = s; *p = *s; *p++ = *s++; i = ++*p;

может сбить с толку.

Черт возьми, после 40 лет C люди до сих пор не могут даже согласиться, если объявление указателя должно быть:

char* p;

или

char *p;
...