Семантический оператор -> в списках (и вообще C ++) - PullRequest
5 голосов
/ 19 декабря 2010

Мое текущее назначение - написание списка с итераторами. Список не является проблемой, так как создание класса итератора.

Из нескольких источников я видел, что у меня есть два оператора для определения в моем классе итераторов: operator* и operator->.

Отлично, пока! Предположим, что моя структура итератора такова

// Nested class of List
class _Iter
{
private:
    ListElem *pCurr;
    const List *pList;

public:
    _Iter(ListElem *pCurr, const List *list)
        : pCurr_(pCurr), pList(list)
    {}

    T& operator*() { return pCurr_->data; }
    T* operator->() { return &**this; }
};

с ListElem, равным

// Nested struct of List
struct ListElem
{
    T data;

    ListElem *next;
    ListElem *prev;
};

Я вижу, что я делаю что-то в корне неправильно (поскольку двойное разыменование этого может привести к & (* pCurr _-> data), которое нельзя разыменовать.

Моя главная проблема - не понять, что -> на самом деле должно делать в этом случае. Должен ли он предоставить пользователю доступ к классу ListElem? Если это так, почему я не могу просто написать

ListElem *operator->() { return pCurr_; }

вместо возврата указателя? Мое понимание этих двух операторов, используемых в моем списке (и, надеюсь, списках STL), таково:

operator*() // Return data pointed to by iterator; pCurr_->data;
operator->() // Grant access to data-holding structure; pCurr; 

Это правильно, или что я не получаю? (И имеет ли -> собственное имя?)

Ответы [ 3 ]

5 голосов
/ 19 декабря 2010

Что бы вы ни делали, (*something).somethingElse должно быть эквивалентно something->somethingElse.Последний - всего лишь короткий синтаксис для первого.Следовательно,

T& operator*() { return pCurr_->data; }
T* operator->() { return &**this; }

хорошо, потому что *this просто разыменовывает this, который имеет тип _Iter*, а не _Iter, поэтому вызов operator*() не выполняется.Затем вы разыменовываете *this, поэтому вы получаете pCurr->data, затем вы берете его адрес, так что вы получаете & pCurr-> data.Но было бы гораздо яснее просто написать:

T& operator*() { return pCurr_->data; }
T* operator->() { return &pCurr->data; }

Теперь, это

ListElem *operator->() { return pCurr_; }

неверно, потому что если operator*() возвращает T&, operator->() должно вернуть T* это то, для чего оно было разработано.Если вы действительно хотите предоставить доступ к ListItem вместо его данных (что может иметь или не иметь смысла в зависимости от дизайна, но в вашем случае это выглядит так, как будто это не так), вам также следует переопределить operator*(), чтобы получить это:

ListElem& operator*() { return *pCurr_; }
ListElem *operator->() { return pCurr_; }

Обратите внимание, что это не требование к языку, это только то, как вы разрабатываете свой класс, чтобы избежать путаницы в интерфейсе.

1 голос
/ 19 декабря 2010

Ваша главная рекомендация должна состоять в том, чтобы

(*iter).hello();
iter->hello();

оба делали одно и то же.Это то, что ожидает пользователь.Возвращение ListElem ничего не дает пользователю.Пользователь даже не должен знать детали реализации ListElem.

1 голос
/ 19 декабря 2010

operator-> дает указатель на объект, на который указывает итератор, в данном случае (по-видимому) pCurr_->data.

T *operator->() { return &(pCurr_->data); }

Должен возвращать адрес объекта, возвращенного operator*() в качестве значения или ссылки.

T &operator*() { return pCurr_->data; }
// or
T &operator*() { return *operator->(); }

operator->() существует для реализации -> с итераторами (с поведением, которое он имеет для указателей) и необходим, поскольку operator* может возвращать объект по значению, а не по ссылке.

Обратите внимание, что вам не нужно хранить указатель на List в итераторе для получения требуемой функциональности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...