Компиляция C ++, когда два класса ссылаются друг на друга

Я пытаюсь написать простую оболочку вокруг указателя соединения, которая вернет его в пул, когда оболочка будет уничтожена, но она не будет компилироваться, потому что ConnectionPool и AutoConn нуждаются в объявлении друг друга.

Я попытался использовать замедление вперед, но это не сработало. Как мне решить эту проблему? (используя g ++)

class Connection {};

class ConnectionPool
{
    Connection *m_c;
public: 
    AutoConn getConn()
    {
        return AutoConn(this, m_c); // by value
    }

    void releaseConnection(Connection *c)
    {
    }
};

class AutoConn
{
    ConnectionPool* m_pool;
    Connection *m_connection;
public:
    AutoConn(ConnectionPool* pool, Connection *c) : m_pool(pool), m_connection(c) {}
    ~AutoConn()
    {
        m_pool->releaseConnection(m_connection);
    }
};

person Omry Yadan    schedule 15.03.2010    source источник
comment
С точки зрения дизайна, это ПЛОХАЯ идея, вы должны попытаться устранить круговую зависимость!   -  person NomeN    schedule 15.03.2010
comment
что ж, это локализованная проблема, и преимущество отсутствия необходимости беспокоиться о разрыве соединения (перед лицом возможных исключений) намного перевешивает всю серьезность этой проблемы проектирования.   -  person Omry Yadan    schedule 15.03.2010


Ответы (7)


Комбинация прямого объявления и отделения объявления от определения членов с циклическими зависимостями работает. Например:

class Connection {};
class ConnectionPool ;

class AutoConn
{

    ConnectionPool* m_pool;
    Connection *m_connection;
public:
    AutoConn(ConnectionPool* pool, Connection *c) : m_pool(pool), m_connection(c) {}
    ~AutoConn() ;  // Not defined here because it accesses unknown members of class Connection
} ;

class ConnectionPool
{
    Connection *m_c;
public: 
    AutoConn getConn()
    {
        return AutoConn(this, m_c); // by value
    }

    void releaseConnection(Connection *c)
    {
    }
};

// Definition of destructor with class Connection member dependencies.
AutoConn::~AutoConn()
{
    m_pool->releaseConnection(m_connection);
}
person Clifford    schedule 15.03.2010

Использовать форвардное объявление:

class Connection {};

class ConnectionPool; //<<<<<<<<<<<<<<<forward declaration

class AutoConn {
//definitions
};

class ConnectionPool {
//definitions
};
person sharptooth    schedule 15.03.2010
comment
Предлагаю вам попробовать его скомпилировать. он не компилируется. - person Omry Yadan; 15.03.2010

реализовать функции после точки, где определены классы

person stefaanv    schedule 15.03.2010

Правильный синтаксис прямого объявления:

class Connection; // no {}

Если вы напишете

class Connection {};

Затем вы определяете класс, и вы не можете определить класс дважды.

Кроме того, не следует ли вам заранее объявлять AutoConn, а не Connection?

person Peter Alexander    schedule 15.03.2010
comment
это не прямое замедление, это просто замедление, поэтому образец был скомпилирован. - person Omry Yadan; 15.03.2010
comment
Он не давал ей определения дважды; это единственное определение Connection. Я предполагаю, что это просто заполнитель для уменьшения количества размещенного кода. - person Clifford; 15.03.2010
comment
О, я думал, что это была ваша попытка предварительного заявления - мои извинения. - person Peter Alexander; 15.03.2010

Прямое объявление только сообщает компилятору, что «такой класс существует». В вашей

AutoConn getConn()

поскольку AutoConn является типом значения, должна быть известна вся структура AutoConn, поэтому предварительное объявление класса не будет работать. Таким образом, вы должны поставить фактическое объявление AutoConn перед ConnectionPool.

В вашем AutoConn тип ConnectionPool упоминается только указателями. В этом случае вся структура ConnectionPool не требуется, поэтому достаточно прямого объявления ConnectionPool.

Поэтому вам нужно переставить классы так:

class Connection;
class ConnectionPool;
class AutoConn { ... };
class ConnectionPool { ... };

Но обратите внимание, что

AutoConn(ConnectionPool* pool, Connection *c) : m_pool(pool), m_connection(c) {}
~AutoConn()
{
    m_pool->releaseConnection(m_connection);
}

эти методы требуют, чтобы компилятор знал члены ConnectionPool, поэтому необходима полная структура. Чтобы решить эту проблему, определение необходимо поместить после ConnectionPool. Таким образом, должны остаться только конструкторы и деструкторы.

class AutoConn {
  ...
  AutoConn(ConnectionPool* pool, Connection *c);
  ~AutoConn();
}
class ConnectionPool { ... };
AutoConn::AutoConn(ConnectionPool* pool, Connection *c) : ... { ... }
AutoConn::~AutoConn() { ... }
person kennytm    schedule 15.03.2010

Возможно, вы захотите передать определение всех методов ConnectionPool и AutoConn на аутсорсинг, т.е.

class ConnectionPool;
class AutoConn {…};

class ConnectionPool {…};

AutoConn ConnectionPool::getConn() {
   …
}
person Alexander Gessler    schedule 15.03.2010

Не включайте ConnectionPool файл заголовка в AutoConn. Просто используйте прямую ссылку, например class ConnectionPool; в файле заголовка AutoConn.

person Naveen    schedule 15.03.2010