2010-03-15 4 views
5

Ich versuche, einen einfachen Wrapper um einen Verbindungszeiger zu schreiben, der ihn an den Pool zurückgibt, wenn der Wrapper zerstört wird, aber nicht kompiliert, da ConnectionPool und AutoConn sich gegenseitig benötigen erklärt werden.Kompilieren von C++, wenn zwei Klassen aufeinander verweisen

Ich versuchte, Vorwärtsverzögerung zu verwenden, aber es hat nicht funktioniert. Wie löse ich das? (Mit 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); 
    } 
}; 
+2

Design weise ist dies eine schlechte Idee, sollten Sie versuchen, die zirkuläre Abhängigkeit zu entfernen! – NomeN

+0

Nun, es ist ein lokales Problem und der Vorteil, sich nicht um die Freigabe der Verbindung kümmern zu müssen (angesichts möglicher Ausnahmen), überwiegt bei weitem die "Schlechtigkeit" dieses Designproblems. –

+0

möglich duplicate von http://stackoverflow.com/questions/2233149/two-classes-and-inline-functions –

Antwort

6

Eine Kombination von Vorwärtsdeklaration und Trennung der Deklaration von der Definition von Mitgliedern mit zirkulären Abhängigkeiten funktioniert. Zum Beispiel:

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); 
} 
+0

großartig, endlich eine echte Antwort :). Vielen Dank! –

4

Sie mit der Vorwärts-Deklaration:

class Connection {}; 

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

class AutoConn { 
//definitions 
}; 

class ConnectionPool { 
//definitions 
}; 
+0

Ich schlage vor, Sie versuchen, es zu kompilieren. es kompiliert nicht. –

3

die Funktionen nach dem Punkt implementieren, wo die Klassen definiert sind

0

Sie möchten vielleicht die Definition aller ConnectionPool und AutoConn Methoden zur Auslagerung , dh

class ConnectionPool; 
class AutoConn {…}; 

class ConnectionPool {…}; 

AutoConn ConnectionPool::getConn() { 
    … 
} 
3

Die co rrect Syntax für eine Vorwärtsdeklaration ist:

class Connection; // no {} 

Wenn Sie schreiben

class Connection {}; 

Dann Sie die Klasse definieren, und Sie können eine Klasse zweimal nicht definieren.

Auch sollten Sie nicht vorwärts sein, AutoConn, nicht Connection deklarieren?

+0

Dies ist keine Vorwärtsverzögerung, es ist nur eine Verzögerung, so dass die Probe kompiliert wird. –

+0

Er hat es nicht zweimal definiert; das ist die * einzige * Definition von Connection. Ich gehe davon aus, dass es nur ein Platzhalter ist, um die Menge des gebuchten Codes zu reduzieren. – Clifford

+0

Oh, ich dachte, das war dein Versuch bei der Vorwärtsdeklaration - meine Entschuldigung. –

0

Nicht enthalten ConnectionPool Header-Datei in AutoConn. Verwenden Sie einfach eine Vorwärtsreferenz wie class ConnectionPool; in der AutoConn Header-Datei.

1

Vorwärtsdeklaration sagt nur dem Compiler "solch eine Klasse existiert". In Ihrem

AutoConn getConn() 

seit AutoConn ein Werttyp ist, die gesamte Struktur der AutoConn bekannt sein müssen, so Vorwärtsdeklaration der Klasse wird nicht funktionieren. Sie müssen also die tatsächliche Deklaration von AutoConn vor ConnectionPool setzen.

In Ihrer AutoConn wird der Typ ConnectionPool nur durch Zeiger verwiesen. In diesem Fall ist die gesamte Struktur von ConnectionPool nicht erforderlich, daher reicht die Vorwärtsdeklaration von ConnectionPool aus.

Deshalb müssen Sie die Klassen in diese Umlagerung:

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

aber feststellen, dass

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

diese Methoden den Compiler erfordern die Mitglieder ConnectionPool zu wissen, so dass eine komplette Struktur benötigt wird. Um dieses Problem zu lösen, muss die Definition nach ConnectionPool platziert werden. Daher sollten nur die Konstruktoren und Destruktoren bleiben.

class AutoConn { 
    ... 
    AutoConn(ConnectionPool* pool, Connection *c); 
    ~AutoConn(); 
} 
class ConnectionPool { ... }; 
AutoConn::AutoConn(ConnectionPool* pool, Connection *c) : ... { ... } 
AutoConn::~AutoConn() { ... } 
+0

das ist auch eine richtige Antwort. +1 für die gute Antwort. –