Циркулярная ссылка на C ++ без указателей - PullRequest
5 голосов
/ 25 августа 2009

Есть ли способ определения циклических ссылок без использования указателей?

Мне нужно что-то вроде этого:

struct A;
struct B {
    A a;
};

struct A {
    B b;
};

Спасибо!

Ответы [ 5 ]

15 голосов
/ 25 августа 2009

Нет, нет. Такая структура будет иметь бесконечный размер.

Вы можете использовать умные указатели ( shared_ptr и weak_ptr ), чтобы избежать прямой манипуляции с указателем, но это все.

13 голосов
/ 25 августа 2009

Вы можете использовать ссылки вместо

struct A;
struct B {
  A& a;
};

struct A {
  B b;
};

Но нет, невозможно создать круговую ссылку без некоторого уровня косвенности. То, что делает ваш пример, даже не создает циклическую ссылку, оно пытается создать рекурсивное определение. Результатом будет структура бесконечного размера и, следовательно, не будет законным.

7 голосов
/ 25 августа 2009

Как это могло работать? Если я правильно помню, значение адреса для ссылки не может быть изменено после установки, поэтому вы не можете определить циклическую ссылку.

Это может работать следующим образом (так же, как пример Джареда плюс определенные конструкторы):

struct A;

struct B {
  A& m_a;
  B(A& a) : m_a(a) {}
};

struct A {
  B m_b;
  //construct B m_b member using a reference to self
  A() : m_b(*this) {}
  //construct B m_b member using a reference to other
  A(A& other) : m_b(other) {}
};
1 голос
/ 25 августа 2009

В C ++ T o означает «объект типа T, а не ссылку на некоторый T (как, например, со ссылочными типами в C # и Java). В коде из вашего вопроса введите A будет иметь подобъект типа B (с именем b), а этот B в свою очередь будет иметь подобъект типа A (с именем a). Теперь этот a будет, в свою очередь, иметь еще один A внутри (снова называемый a), который затем имеет еще один B, который ...

Нет, это не будет работать.

Что вам, вероятно, нужно, это то, что A ссылается на B, что, в свою очередь, ссылается на A. Это можно сделать с помощью указателей:

struct A;
struct B {
    A* a;
    B(A*);
};

struct A {
    B* b;
    A(B* b_) : b(b_)  { if(b) b.a = this; }
};

B::B(A* a_) : : a(a_) { if(a) a.b = this; }

Не думаю, что это можно сделать с помощью ссылок.

0 голосов
/ 05 августа 2014

Решение ChrisW можно обобщить примерно так:

template <class defaultState> struct Context;

struct State1 {
    Context<State1>& mContext;
    State1(Context<State1> & ref) : mContext(ref) {}
};

template <class TDefaultState>
struct Context {
    TDefaultState mState;
    Context() : mState(*this) {}
};

Теперь это позволяет вам

Context<State1> demo;

Далее, у State может быть также некоторый вспомогательный код шаблона

template <class State>
struct TState {
    typedef Context<State> TContext;
    typedef TState<State> TBase;
    Context<State> & mContext;
    TState(Context<State> &ref) : mContext(ref) {}
};

struct State2 : TState<State2> {
    State2(TContext & ref) : TBase(ref) {}
};
struct State3 : TState<State3> {
    State3(TContext & ref) : TBase(ref) {}
};

Что теперь позволяет вам делать любой из

Context<State2> demo2;
Context<State3> demo3;
...