С++ перегрузка операторов разыменования

Я относительно не знаком с С++, все еще пытаюсь получить синтаксис. Я рассматриваю несколько примеров перегрузки операторов, а в последнее время - реализации интеллектуальных указателей. Вот действительно общий пример, на который я смотрю:

template < typename T > class SP
{
    private:
    T*    pData; // Generic pointer to be stored
    public:
    SP(T* pValue) : pData(pValue)
    {
    }
    ~SP()
    {
        delete pData;
    }

    T& operator* ()
    {
        return *pData;
    }

    T* operator-> ()
    {
        return pData;
    }
};

При перегрузке оператора разыменования, почему это тип T &? Аналогично, при перегрузке разложения структуры, почему это тип T *?

Ответы

Ответ 1

Перегрузка разрыва оператора (*) работает как любая другая перегрузка оператора. Если вы хотите изменить значение разыменования, вам нужно вернуть неконстантную ссылку. Таким образом, *sp = value фактически изменит значение, на которое указывает sp.pData, а не временное значение, сгенерированное компилятором.

Перегрузка оператора структурного разыменования (->) является частным случаем перегрузки оператора. Оператор фактически вызывается в цикле до тех пор, пока не будет возвращен реальный указатель, а затем этот реальный указатель будет разыменован. Я предполагаю, что это был единственный способ, которым они могли придумать, чтобы реализовать его, и это оказалось немного хаки. Однако у него есть некоторые интересные свойства. Предположим, что у вас были следующие классы:

struct A {
    int foo, bar;
};

struct B {
    A a;
    A *operator->() { return &a; }
};

struct C {
    B b;
    B operator->() { return b; }
};

struct D {
    C c;
    C operator->() { return c; }
};

Если у вас есть объект d типа d, вызов d->bar сначала вызовет D::operator->(), затем C::operator->(), а затем B::operator->(), который, наконец, вернет реальный указатель на struct A, и его член bar разыменован обычным образом. Обратите внимание, что в следующем:

struct E1 {
    int foo, bar;
    E1 operator->() { return *this; }
};

Вызов e->bar, где e имеет тип E1, создает бесконечный цикл. Если вы действительно хотите разыменовать e.bar, вам нужно будет сделать это:

struct E2 {
    int foo, bar;
    E2 *operator->() { return this; }
};

Подводя итог:

  • При перегрузке оператора разыменования тип должен быть T&, потому что это необходимо для изменения значения, на которое указывает pData.
  • При перегрузке разыменования структуры тип должен быть T*, потому что этот оператор является частным случаем, и именно так оно и работает.

Ответ 2

Целью оператора разыменования является разыменование указателя и возврат ссылки на объект. Следовательно, он должен вернуть ссылку. Вот почему это T &.

Цель обращения оператора (- > ) - вернуть указатель и, следовательно, вернуть T *.