устранение перегрузок операторов присваивания; Вы можете переназначить ссылку? - PullRequest
0 голосов
/ 09 декабря 2011

Если у вашего класса есть ссылочная переменная, то ваш перегруженный оператор присваивания должен быть записан.

У меня сложилось впечатление, что вы можете установить ссылку на экземпляр только один раз, и поэтому не можете сделать что-то вроде:

MyClass& MyClass::operator=(const MyClass& rhs) 
{
    if (&rhs != this)
    {
        myReference = rhs.myReference;
    }
    return *this;
}

Как вы решаете эту проблему?

РЕДАКТИРОВАТЬ - ОК, поэтому мне сказали, что вы не можете использовать оператор присваивания для класса со ссылкой, хорошо. Но тогда почему Visual Studio позволяет мне это делать? Программа запускается и все.

Ответы [ 5 ]

1 голос
/ 09 декабря 2011

Нет, вы не можете переустановить ссылку.

Рассмотрим:

int a = 42, b = 43;
int &ar = a;
ar = b;

Как компилятор может узнать, что вы пытаетесь переустановить ar для ссылки на b, а не установить значение из a в 43?

Вы решаете эту «проблему», используя указатель, а не ссылку.

РЕДАКТИРОВАТЬ: В соответствии с вашими правками,

ОК, поэтому мне сказали, что вы не можете использовать оператор присваивания в классе с Ссылка, хорошо. Но тогда почему Visual Studio позволяет мне это делать? программа запускается и все.

Предпосылка вашего заключения неверна. Вы можете использовать оператор присваивания для класса, который содержит ссылку. То, что вы не можете сделать, это переместить ссылку. Как показано в моем коде выше, если вы попытаетесь переназначить ссылку, используя ar = a;, вы не будете переопределять то, к чему относится ar, но измените значение того, к чему относится ar.

Visual Studio "позволяет вам делать это" без труда. Это недоразумение - именно то, что позволяет вам делать Visual Studio. Это не позволяет вам переустановить ссылку. Это позволяет вам изменить значение референта. Вот пример, который, я надеюсь, прояснит, что это значит.

#include <iostream>
#include <string>
using namespace std;

class Foo
{
public:
    void dump() const
    {
        cout << "Foo instance " << showbase << this << "\n";
    }
};

class Bar
{
public:
    Bar(Foo& foo) : foo_(foo) {}
    Bar& operator=(const Bar& rhs) 
    {
        foo_ = rhs.foo_;
        return * this;
    }

    void dump() const
    {
        cout << showbase << "Bar instance " << this << "\t";
        foo_.dump();
    }

private:
    Foo& foo_;
};

int main()
{
    cout << "foo1: ";
    Foo foo1;
    foo1.dump();

    cout << "foo2: ";
    Foo foo2;
    foo2.dump();

    cout << "bar1 :";
    Bar bar1(foo1);
    bar1.dump();

    cout << "bar2 :";
    Bar bar2(foo2);
    bar2.dump();

    bar2 = bar1;
    cout << "bar2 after assign :";
    bar2.dump();
}

Приведенный выше код устанавливает 2 Foo объекта (foo1 и foo2) и создает 2 Bar объектов, каждый из которых имеет ссылку на свой Foo. Bar имеет operator=, который выполняет следующее:

foo_ = rhs.foo_;

Если бы C ++ позволил вам переназначить ссылки таким образом, foo_ теперь будет ссылаться на другой экземпляр Foo. Но это не так. Это не меняет того, к чему относится foo_. Вместо этого он вызывает operator= на самом Foo. Запустите приведенный выше код, и вы увидите, что адрес Foo в bar2 никогда не меняется. Если бы вы могли изменить ссылки, это изменилось бы.

0 голосов
/ 09 декабря 2011

Ваш код работает как написано, однако вы не переназначаете ссылку.

myReference = rhs.myReference;

Назначит объект, на который ссылается rhs.myReference, myReference. Следовательно, предполагая, что до присвоения &myReference != &(rhs.myReference) было истинно, оно все равно будет истинно после назначения, однако объекты по этим адресам будут содержать те же значения (поэтому myReference == rhs.myReference, если operator== определено для типа и работает в давай скажем безудержный путь). Переназначение ссылки (что невозможно) будет означать, что после присвоения &myReference == &(rhs.myReference) будет верно. Поэтому реальный вопрос заключается в том, что вы хотите сделать: хотите ли вы скопировать объект, на который указывает rhs.myReference, в объект, на который указывает this->myReference (в этом случае ваш код в порядке), или вы хотите создать this->myReference ссылаются на тот же объект, что и rhs.myReference (что невозможно со ссылками, поэтому вам необходимо использовать указатели).

0 голосов
/ 09 декабря 2011

Ссылки не могут быть восстановлены.(Ну, размещение new может, но НЕ ДЕЛАЙТЕ ЭТОГО!)

Но код допустим, даже если он не делает того, о чем вы думаете.Это не повторное связывание или переназначение ссылки, скорее присвоение референту (цель ссылки).

0 голосов
/ 09 декабря 2011

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

0 голосов
/ 09 декабря 2011

Вы можете создать оператор присваивания с классом, который использует ссылку, но эта строка:

myReference = rhs.myReference;

Не переназначает ссылку.Если переназначить вещь, на которую ссылается ссылка.Таким образом, после этого назначения myReference и rhs.myReference теперь не ссылаются на один и тот же объект.Но вещи, на которые они ссылаются, теперь имеют эквивалентные значения (или любое другое присвоение для этого типа).

Если вам нужна переназначаемая ссылка, используйте указатель.Вот для чего они.На самом деле, в современном C ++ это практически единственное использование для необработанного указателя.Если объект, на который вы ссылаетесь, выделяется динамически, вы должны поместить его в shared_ptr и сделать myReference либо другим shared_ptr, либо weak_ptr.

...