【问题标题】:using vectors of class type A in class B在 B 类中使用 A 类类型的向量
【发布时间】:2012-10-24 18:38:21
【问题描述】:

我已经创建了一个 A 类和一个 B 类,我试图在 A 中设置一个 B 类型的向量,在 B 中设置一个 A 类型的向量:

A 类标头:

#ifndef A_H_
#define A_H_

#include "B.h"
#include <vector>
using namespace std;
class A {

public:
    vector<B> bvector; // here is the error
    A();
};

#endif /* A_H_ */

B 类标头:

#ifndef B_H_
#define B_H_
#include "A.h"
#include <vector>

using namespace std;

class B {
vector<A> aVector; //Here is the error

public:
    B();
};

#endif /* B_H_ */

但我收到以下错误:

"..\src/B.h:16:8: 错误:'A' 未在此范围内声明

..\src/B.h:16:9: 错误:模板参数 1 无效

..\src/B.h:16:9: 错误:模板参数 2 无效"

如果我删除 B 中的坏行,哪个会翻转到 A.h。我做错了什么?

【问题讨论】:

  • 您必须对其中一个进行前向声明(但由于您可以为两者都这样做,因此您应该在两者中都这样做,而不是 #include 任何一个标题)。
  • @SethCarnegie 前向声明仅适用于指针:stackoverflow.com/questions/37346/…
  • @num3ric 它适用于不完整类型就足够的任何地方,包括vector 的声明。
  • @SethCarnegie,你说得对,它适用于任何不完整类型就足够的地方,但 std::vector 需要完整类型。实例化具有不完整类型的大多数标准库模板是未定义的行为,请参阅 [res.on.functions]/2
  • @JonathanWakely 好烂。

标签: c++ class vector


【解决方案1】:

我创建了一个 A 类和一个 B 类,我试图设置一个向量类型 A中的B,B中的A类型向量

您正在班级之间创建circular dependency。这通常是一件坏事,尤其是在 C++ 中。

为了编译A,编译器需要知道B的定义(#include "B.h")。不幸的是,B 标头包含对 A 类的引用(这里是循环引用)。 编译器无法处理这种情况,因为当前 TU 中已经包含 A 标头(请参阅包含保护)。

尽管经常有循环引用是糟糕设计的征兆,但您最终还是可以使用前向声明来克服这个问题。 例如,您可以这样修改 B:

#ifndef B_H_
#define B_H_

#include <vector>

using namespace std;
class A; //forward declaration of class A
class B {
vector<A*> aVector; //note that you must only use pointer to A now 

public:
    B();
};

#endif /* B_H_ */

使用前向声明基本上告诉编译器类型 A 将在其他地方定义。编译器可以依赖这个事实,但它对 A 一无所知(特别是它忽略了 A 的大小及其方法)。 所以在 B 内部,如果你前向声明了 A,你只能使用指向 A 类的指针(指针总是相同的大小),你不能从 B 内部调用 A 类的任何方法。

【讨论】:

  • 非常感谢,对我帮助很大。
【解决方案2】:

这里的诀窍是您需要转发声明两个类之一。不幸的是,前向声明不允许您在标头中使用完整类型——它们只允许您使用指向该类型的指针。所以即使在这种情况下也行不通。

您可以使用 PIMPL idiom (Pointer to IMPlementation)(google that)来规避这个问题。

PIMPL 习惯用法基本上会让您创建第三种类型。然后你的 B 类型将持有一个指向包含 A 类型向量的实现类的指针。应该在 B 中保存的向量的所有操作都将转发到实际保存 A 向量的新实现类。

例如:

//Forward declare impl class.
class BImpl;

class B {
    private:
        BImpl* impl;

    public:
        void PrintVector() { impl->PrintVector(); }
};

class A {
    private:
        std::vector<B> vec;

    public:
        void PrintVector() { /* Do printing */ }
};

class BImpl {
    private:
        std::vector<A> vec;

    public:
        void PrintVector() { /* Do Printing */ }
};

我没有包含这些类的构造函数或填充方法,但您应该了解大致的概念 :)

【讨论】:

  • 如果我理解正确,解决方案是绕过,因此我在 c++ 中做错了什么?
  • 所以编译器(预处理器?)从 A 开始,包括 B,而 B 本身没有包含语句。然后它处理 BImpl,包括 A,但 A 已经定义,所以什么也没有发生。问题已解决。对吗?
  • @user1778406 - 你没有做错任何事。在某些情况下,循环引用是生活中的事实。它们有时可能表明您应该重新考虑您的设计——它可能过于复杂。如果你能把它们设计出来,那就太好了,否则,这是一个很好的解决方案。这实际上是一种将实现与声明分离的解决方案,以在构建时简化编译单元之间的依赖关系,但它也适用于此。
  • @num3ric - 差不多。 B 可以使用 BImpl 因为它只使用一个前向声明的指针。 A 可以使用 B,因为首先定义了 B。 BImpl 可以使用 A,因为 A 是在 BImpl 之前定义的。您可以将其拆分为多个文件,它会正常工作 - 为了便于演示,我只是在一个地方完成了所有操作。
【解决方案3】:

你有一个循环依赖。

A.h包含B.h,其中包含A.h,但是A_H_已经定义了保护,所以A.h的内容被跳过,所以B.h的内容被处理,指的是@987654327 @ 但它尚未定义,因为您尚未处理 A.h 中的任何内容,除了 A.h 顶部的 #define#include 指令

【讨论】:

    猜你喜欢
    • 2022-11-07
    • 2021-08-25
    • 1970-01-01
    • 1970-01-01
    • 2017-12-28
    • 1970-01-01
    • 2011-09-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多