开发者

Mutual class instances in C++

开发者 https://www.devze.com 2022-12-24 08:40 出处:网络
What is the issue with this code? Here we have two files: classA.h and classB.h classA.h: #ifndef _class_a_h_

What is the issue with this code? Here we have two files: classA.h and classB.h

classA.h:

#ifndef _class_a_h_
#define _class_a_h_

#include 开发者_运维技巧"classB.h"

class B; //????

class A
{
public:
    A() {
        ptr_b = new B(); //????
    }
    
    virtual ~A() {
        if(ptr_b) delete ptr_b; //????
                    num_a = 0;
    }
    
    int num_a;
    B* ptr_b; //????
};

#endif //_class_a_h_

classB.h:

#ifndef _class_b_h_
#define _class_b_h_

#include "classA.h"

class A; //????

class B
{
public:     
    B() { 
        ptr_a = new A(); //????
                    num_b = 0;
    }
    
    virtual ~B() { 
        if(ptr_a) delete ptr_a; //????
    }
            
    int num_b;
    A* ptr_a; //????
};

#endif //_class_b_h_

when I try to compile it, the compiler (g++) says:

classB.h: In constructor ‘B::B()’:

classB.h:12: error: invalid use of incomplete type ‘struct A’

classB.h:6: error: forward declaration of ‘struct A’

classB.h: In destructor ‘virtual B::~B()’:

classB.h:16: warning: possible problem detected in invocation of delete operator:

classB.h:16: warning: invalid use of incomplete type ‘struct A’

classB.h:6: warning: forward declaration of ‘struct A’

classB.h:16: note: neither the destructor nor the class-specific operator delete will be

called, even if they are declared when the class is defined.


You cannot create instances of an incomplete type (the compiler doesn't know anything about the class!)

You need to move the definitions of your functions (the constructor of A and B) into a C++ file that can include both headers (or into several C++ files, if you follow the convention that you have one class per file).

That having been said, your code as written has a serious problem: every A creates an instance of B and every B creates and instance of A. You will end up with an infinite recursion and you will eventually run out of memory.

Two minor nitpicks: you do not need to test whether a pointer is null before calling delete on it (it is safe to delete a null pointer), and you need to change your include guards (names beginning with an underscore in the global namespace are reserved to the implementation).


EDIT: Read James McNellis's answer first -- this is a code example of what you'd have to do. But the recursion is the bigger point and he deserves any upvotes for that particular point -- not me :)

You can't use inline functions here as the full definition for classes A and B is not available when you're declaring them inline. Declare them as normal functions and you'll be fine with the forward declarations you have.

classA.h

#ifndef _class_a_h_
#define _class_a_h_

#include "classB.h"

class B; //????

class A
{
public:
    A();
    virtual ~A();
    int num_a;
    B* ptr_b;
};

#endif //_class_a_h_

classB.h

#ifndef _class_b_h_
#define _class_b_h_

#include "classA.h"

class B
{
public:     
    B();
    virtual ~B();
    int num_b;
    A* ptr_a;
};

#endif //_class_b_h_

classes.cpp

#include "classA.h"
#include "classB.h"

A::A() {
    ptr_b = new B(); //????
}

A::~A() {
    if(ptr_b) delete ptr_b; //????
}

B::B() { 
    ptr_a = new A; //????
}

B::~B() { 
    if(ptr_a) delete ptr_a; //????
}


classB.h: In constructor ‘B::B()’:

classB.h:12: error: invalid use of incomplete type ‘struct A’

A is not fully defined. You've only given it a prototype (class A;).

classB.h:6: error: forward declaration of ‘struct A’

classB.h: In destructor ‘virtual B::~B()’:

I think this is the same problem. It needs to know how A is defined so it knows how much memory to free up.

Refactor your code to remove the circular dependency. (A creates B, and B creates A... creates B, creates A, creates B...)


The simple solution is to pull your member function definitions out-of-line and into the files classA.cpp and classB.cpp. Remove the mutual includes from the header files and instead insert those into the .cpp files.

Anywhere class A or B is used more than just in name (that is, other than just naming its pointer or reference types), a complete class declaration must already be present. With your current include/inline design, one or the other will necessarily not be complete. By splitting the class implementations out into .cpp files, you allow the declarations to complete gracefully before instantiating objects of type A or B.

0

精彩评论

暂无评论...
验证码 换一张
取 消