C++ 循环包含依赖项/转发声明

C++ 循环包含依赖项/转发声明,c++,C++,我看到的所有循环依赖的解决方案都包含依赖项,只是在“这个特殊情况”中说,完整的类定义是不必要的,因为“您”只使用指向该类的指针 我遇到了这个问题,并使用前向声明修复了它 我想知道当你需要两个类中另一个类的具体定义时,你应该怎么做 另外,为什么使用指向类的指针可以使用前向声明而不是类定义?在什么情况下,您需要预先为这两个类指定规范 一种不可能的情况如下: class A { B m_b; }; class B { A m_a; }; 但这是不可能的,因为类A的大小取决于类B的大

我看到的所有循环依赖的解决方案都包含依赖项,只是在“这个特殊情况”中说,完整的类定义是不必要的,因为“您”只使用指向该类的指针

我遇到了这个问题,并使用前向声明修复了它

我想知道当你需要两个类中另一个类的具体定义时,你应该怎么做


另外,为什么使用指向类的指针可以使用前向声明而不是类定义?

在什么情况下,您需要预先为这两个类指定规范

一种不可能的情况如下:

class A
{
    B m_b;
};

class B
{
    A m_a;
};
但这是不可能的,因为类A的大小取决于类B的大小,但类B的大小取决于类A的大小。你还将得到一个无限级数
A myA;myA.m_b.m_a.m_b.m_a….
当您尝试构造其中一个时


如果你使用指针,你不需要知道它们的大小;指针的大小始终相同,具体取决于您所在的平台。因为堆中的对象需要显式地创建,所以这个系列就消失了。

在什么情况下,两个类都需要事先知道的规范

一种不可能的情况如下:

class A
{
    B m_b;
};

class B
{
    A m_a;
};
但这是不可能的,因为类A的大小取决于类B的大小,但类B的大小取决于类A的大小。你还将得到一个无限级数
A myA;myA.m_b.m_a.m_b.m_a….
当您尝试构造其中一个时

如果你使用指针,你不需要知道它们的大小;指针的大小始终相同,具体取决于您所在的平台。由于堆中的对象需要显式创建,因此该系列将消失

我想知道当你需要具体的信息时,你应该怎么做 两个类中另一个类的定义

在现代编译器中,它可以通过前向声明和延迟定义来实现。许多较旧的编译器只允许指向前向声明类型的指针和引用

下面是一个人为的例子:

A.hpp

class B;

class A
{
public:
    int32_t Value;

    A(int32_t value) : Value(value) { }

    int32_t Add(B b) const;
}
#include "A.hpp"

class B
{
public:
    int32_t Value;

    B(int32_t value) : Value(value) { }

    int32_t Sub(A a) const;
}
#include "A.hpp"
#include "B.hpp"

inline int32_t A::Add(B b) const
{
    return this->Value + b.Value;
}

inline int32_t B::Sub(A a) const
{
    return this->Value - a.Value;
}
B.hpp

class B;

class A
{
public:
    int32_t Value;

    A(int32_t value) : Value(value) { }

    int32_t Add(B b) const;
}
#include "A.hpp"

class B
{
public:
    int32_t Value;

    B(int32_t value) : Value(value) { }

    int32_t Sub(A a) const;
}
#include "A.hpp"
#include "B.hpp"

inline int32_t A::Add(B b) const
{
    return this->Value + b.Value;
}

inline int32_t B::Sub(A a) const
{
    return this->Value - a.Value;
}
AB.hpp

class B;

class A
{
public:
    int32_t Value;

    A(int32_t value) : Value(value) { }

    int32_t Add(B b) const;
}
#include "A.hpp"

class B
{
public:
    int32_t Value;

    B(int32_t value) : Value(value) { }

    int32_t Sub(A a) const;
}
#include "A.hpp"
#include "B.hpp"

inline int32_t A::Add(B b) const
{
    return this->Value + b.Value;
}

inline int32_t B::Sub(A a) const
{
    return this->Value - a.Value;
}
另外,为什么使用指向类的指针可以使用转发 声明而不是类定义

转发声明只是编译器的名称。该概念存在,因此您可以使用尚未定义的类型。这是必要的,因为C++解析代码的方式,它是C语言的一个伪品,它继承了大量的代码。C++解析器实际上只是前向文本处理器,当你使用<宏>时,C++代码中包含文本。这是一个概念上简单的模型,使C/C++编译器在早期更容易编写。与C#/Java相比,C#/Java只需使用/import,就可以用简单的语法在类之间创建循环依赖关系

指针实际上只是整数,类似于
short
int
,但具有语言强制的特殊语义,并且在编译时根据CPU体系结构确定了固定大小。这使得编译器处理指针声明非常简单

前向声明有助于循环依赖关系和实现隐藏(这也恰好加快了编译时间)。考虑一下。没有转发声明,就没有类型安全的方法来隐藏实现细节

我想知道当你需要具体的信息时,你应该怎么做 两个类中另一个类的定义

在现代编译器中,它可以通过前向声明和延迟定义来实现。许多较旧的编译器只允许指向前向声明类型的指针和引用

下面是一个人为的例子:

A.hpp

class B;

class A
{
public:
    int32_t Value;

    A(int32_t value) : Value(value) { }

    int32_t Add(B b) const;
}
#include "A.hpp"

class B
{
public:
    int32_t Value;

    B(int32_t value) : Value(value) { }

    int32_t Sub(A a) const;
}
#include "A.hpp"
#include "B.hpp"

inline int32_t A::Add(B b) const
{
    return this->Value + b.Value;
}

inline int32_t B::Sub(A a) const
{
    return this->Value - a.Value;
}
B.hpp

class B;

class A
{
public:
    int32_t Value;

    A(int32_t value) : Value(value) { }

    int32_t Add(B b) const;
}
#include "A.hpp"

class B
{
public:
    int32_t Value;

    B(int32_t value) : Value(value) { }

    int32_t Sub(A a) const;
}
#include "A.hpp"
#include "B.hpp"

inline int32_t A::Add(B b) const
{
    return this->Value + b.Value;
}

inline int32_t B::Sub(A a) const
{
    return this->Value - a.Value;
}
AB.hpp

class B;

class A
{
public:
    int32_t Value;

    A(int32_t value) : Value(value) { }

    int32_t Add(B b) const;
}
#include "A.hpp"

class B
{
public:
    int32_t Value;

    B(int32_t value) : Value(value) { }

    int32_t Sub(A a) const;
}
#include "A.hpp"
#include "B.hpp"

inline int32_t A::Add(B b) const
{
    return this->Value + b.Value;
}

inline int32_t B::Sub(A a) const
{
    return this->Value - a.Value;
}
另外,为什么使用指向类的指针可以使用转发 声明而不是类定义

转发声明只是编译器的名称。该概念存在,因此您可以使用尚未定义的类型。这是必要的,因为C++解析代码的方式,它是C语言的一个伪品,它继承了大量的代码。C++解析器实际上只是前向文本处理器,当你使用<宏>时,C++代码中包含文本。这是一个概念上简单的模型,使C/C++编译器在早期更容易编写。与C#/Java相比,C#/Java只需使用/import,就可以用简单的语法在类之间创建循环依赖关系

指针实际上只是整数,类似于
short
int
,但具有语言强制的特殊语义,并且在编译时根据CPU体系结构确定了固定大小。这使得编译器处理指针声明非常简单


前向声明有助于循环依赖关系和实现隐藏(这也恰好加快了编译时间)。考虑一下。如果没有转发声明,就没有类型安全的方法来隐藏实现细节。

我想到的一个例子是,对象“A”创建对象“B B”,但在构建时或在以后的某个点将其自身的副本传递给它。但是,“对象B”有A类型的成员变量。我很确定,任何你想做的情况都会被认为是糟糕的编程,但这正是困扰我的问题。谢谢你的全面回答!“传递一个副本”-您可以声明一个函数,该函数包含一个不完整的类,该类通过值作为参数或返回类型传递;在类定义完成之前,您无法定义或调用它。So
A类;B类{公共:无效进程(A);}很好。(即使在这里,您也可以使用
常量A&
)我想到的一个例子是对象“A”创建对象“B”,但它是passe