【问题标题】:Mutual class instances in C++C++ 中的相互类实例
【发布时间】:2011-02-02 07:05:57
【问题描述】:

这段代码有什么问题? 这里我们有两个文件:classA.h 和 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_

当我尝试编译它时,编译器 (g++) 说:

classB.h:在构造函数'B::B()'中:

classB.h:12: 错误:无效使用不完整类型‘struct A’

classB.h:6: 错误:'struct A' 的前向声明

classB.h:在析构函数‘virtual B::~B()’中:

classB.h:16:警告:在调用删除运算符时检测到可能的问题:

classB.h:16:警告:不完整类型“struct A”的使用无效

classB.h:6:警告:“结构 A”的前向声明

classB.h:16:注意:析构函数和特定于类的运算符 delete 都不会

调用,即使它们在定义类时声明。

【问题讨论】:

  • 每当类如此耦合时,您的设计可能需要一些重构。
  • @GMan:除了实现迭代器模式之外,非常同意。 +1。

标签: c++ class pointers


【解决方案1】:

您不能创建不完整类型的实例(编译器对类一无所知!)

您需要将函数的定义(A 和 B 的构造函数)移动到可以包含两个头文件的 C++ 文件中(或者移动到多个 C++ 文件中,如果您遵循每个文件有一个类的约定)。

话虽如此,你写的代码有一个严重的问题:每个 A 创建一个 B 的实例,每个 B 创建一个 A 的实例。你最终会得到一个无限递归,你最终会耗尽内存。

两个小问题:您不需要在调用 delete 之前测试指针是否为空(删除空指针是安全的),并且您需要更改包含保护(名称以下划线开头的全局命名空间保留给实现)。

【讨论】:

    【解决方案2】:

    编辑:首先阅读 James McNellis 的答案 - 这是您必须执行的代码示例。但是递归是更重要的一点,他应该为那个特定的点点赞——不是我:)

    您不能在此处使用内联函数,因为当您将 A 和 B 类声明为内联时,它们的完整定义不可用。将它们声明为普通函数,您就可以使用前向声明了。

    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; //????
    }
    

    【讨论】:

    • 但是...注意无限递归!
    • @Drew Hall:好点——没想到这一点。但不会在这里窃取詹姆斯·麦克内利斯的积分——他应得的。
    • 我认为它根本不起作用。你试过自己编译吗?
    • 你给了我太多的信任。我对你的回答做了一个小的更正(virtual 说明符只出现在函数声明中,而不是定义中);我希望你不要介意。 @SepiDev:现在的代码将编译。您仍然需要解决此页面上提到的其他问题。
    • @James McNellis:Doh!谢谢! @SepiDev:尝试像 James McNellis 所做的那样删除虚拟说明符。
    【解决方案3】:

    classB.h:在构造函数'B::B()'中:

    classB.h:12:错误:无效使用 不完整类型‘struct A’

    A 未完全定义。你只给了它一个原型 (class A;)。

    classB.h:6: 错误:前向声明 '结构A'

    classB.h: 在析构函数‘virtual B::~B()':

    认为这是同样的问题。它需要知道A 是如何定义的,以便知道要释放多少内存。

    重构您的代码以消除循环依赖。 (A创造B,B创造A……创造B,创造A,创造B……)

    【讨论】:

      【解决方案4】:

      简单的解决方案是将您的成员函数定义从外部提取到文件 classA.cpp 和 classB.cpp 中。从头文件中删除相互包含,并将它们插入到 .cpp 文件中。

      类 A 或 B 的使用不只是在名称中(也就是说,除了命名其指针或引用类型之外),必须已经存在完整的类声明。对于您当前的包含/内联设计,其中一个不一定是完整的。通过将类实现拆分为 .cpp 文件,您可以让声明在实例化 A 或 B 类型的对象之前优雅地完成。

      【讨论】:

        猜你喜欢
        • 2018-03-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-03
        • 1970-01-01
        • 2013-07-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多