Компиляция C ++, когда два класса ссылаются друг на друга - PullRequest
5 голосов
/ 15 марта 2010

Я пытаюсь написать простую оболочку вокруг указателя соединения, которая будет возвращать его в пул при уничтожении оболочки, но она не будет компилироваться, потому что 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);
    }
};

Ответы [ 7 ]

6 голосов
/ 15 марта 2010

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

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);
}
4 голосов
/ 15 марта 2010

Использовать предварительную декларацию:

class Connection {};

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

class AutoConn {
//definitions
};

class ConnectionPool {
//definitions
};
3 голосов
/ 15 марта 2010

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

class Connection; // no {}

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

class Connection {};

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

Кроме того, разве вы не должны быть вперед, объявляя AutoConn, а не Connection?

3 голосов
/ 15 марта 2010

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

1 голос
/ 15 марта 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() { ... }
0 голосов
/ 15 марта 2010

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

0 голосов
/ 15 марта 2010

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

class ConnectionPool;
class AutoConn {…};

class ConnectionPool {…};

AutoConn ConnectionPool::getConn() {
   …
}
...