【问题标题】:How to include files for virtual methods?如何包含虚拟方法的文件?
【发布时间】:2016-07-12 23:41:45
【问题描述】:

我有两个类层次结构:As 和 Bs。 As 有一个虚方法返回Bs,Bs 有一个虚方法返回As。

啊.h

#pragma once
class B;

struct A
{
    virtual B* f() = 0;
};

A1.h

#pragma once
#include "A.h"
#include "B1.h"

struct A1 : public A
{
    B1* f() override;
};

B.h

#pragma once
class A;

struct B
{
    virtual A* g() = 0;
};

B1.h

#pragma once
#include "B.h"
#include "A1.h"

struct B1 : public B
{
    A1* g() override;
};

在 VS2015 中编译时不会出错

overriding virtual function return type differs and is not covariant from A

带有类前向声明​​

class A1 : public A;

它应该工作。但是 C++ 不支持这样的类前向声明​​。如何解决?

【问题讨论】:

  • 包括警卫。研究“包括警卫”。它们是嵌入到头文件中以防止循环依赖的微型纳米机器人;或者您可以使用自己的定义。 :-)
  • 他们不再教“ifdefs 的舞蹈”了吗?还是我们现在都在使用这些新奇的编译用语?
  • @ThomasMatthews 忘了写包含守卫。使用包含守卫,问题也存在。
  • 似乎我没有清楚地描述这个问题,没有人得到真正的问题。
  • @user1899020 也许包括实际的错误信息?

标签: c++ include virtual


【解决方案1】:

在处理循环引用时,在适当的情况下使用前向声明而不是#include 语句。

另外,头文件本身应该有保护,以避免在#include'd 多次声明其内容时多次声明其内容。守卫本身可以使用#ifndef/#define 对或#pragma once,具体取决于您的编译器。


啊.h

#ifndef A_H
#define A_H

struct B;

struct A
{
    virtual B* f() = 0;
};

#endif

或者:

#pragma once

struct B;

struct A
{
    virtual B* f() = 0;
};

A1.h:

#ifndef A1_H
#define A1_H

#include "A.h"

struct B1;

struct A1 : public A
{
    B1* f() override;
};

#endif

或者:

#pragma once

#include "A.h"

struct B1;

struct A1 : public A
{
    B1* f() override;
};

A2.h

#ifndef A2_H
#define A2_H

#include "A.h"

struct B2;

struct A2 : public A
{
    B2* f() override;
};

#endif

或者:

#pragma once

#include "A.h"

struct B2;

struct A2 : public A
{
    B2* f() override;
};

B.h

#ifndef B_H
#define B_H

struct A;

struct B
{
    virtual A* g() = 0;
};

#endif

或者:

#pragma once

struct A;

struct B
{
    virtual A* g() = 0;
};

B1.h

#ifndef B1_H
#define B1_H

#include "B.h"

struct A1;

struct B1 : public B
{
    A1* g() override;
};

#endif

或者:

#pragma once

#include "B.h"

struct A1;

struct B1 : public B
{
    A1* g() override;
};

B2.h

#ifndef B2_H
#define B2_H

#include "B.h"

struct A2;

struct B2
{
    A2* g() override;
};

#endif

或者

#pragma once

#include "B.h"

struct A2;

struct B2
{
    A2* g() override;
};

满足前向声明类的#include语句应该在方法实现源文件中使用,而不是在它们的声明头文件中:

A1.cpp:

#include "A1.h"
#include "B1.h" // <--

B1* A1::f()
{
    return new B1; // or wherever the B1 object comes from...
}

A2.h

#include "A2.h"
#include "B2.h" // <--

B2* A2::f()
{
    return new B2; // or wherever the B2 object comes from...
}

B1.cpp

#include "B1.h"
#include "A1.h" // <--

A1* B1::g()
{
    return new A1; // or wherever the A1 object comes from...
}

B2.cpp

#include "B2.h"
#include "A2.h" // <--

A2* B2::g()
{
    return new A2; // or wherever the A2 object comes from...
}

话虽如此,向前声明结构是处理循环引用的唯一方法。但是由于您无法转发声明层次结构,因此我认为如果不重新考虑您的设计就无法解决您的问题。如果这不是一个选项,您将不得不远离使用协变返回值。

啊.h

#pragma once

struct B;

struct A
{
    virtual B* f() = 0;
};

A1.h:

#pragma once

#include "A.h"

struct A1 : public A
{
    B* f() override;
};

A2.h

#pragma once

#include "A.h"

struct A2 : public A
{
    B* f() override;
};

B.h

#pragma once

struct A;

struct B
{
    virtual A* g() = 0;
};

B1.h

#pragma once

#include "B.h"

struct B1 : public B
{
    A* g() override;
};

B2.h

#pragma once

#include "B.h"

struct B2
{
    A* g() override;
};

A1.cpp:

#include "A1.h"
#include "B1.h" // <--

B* A1::f()
{
    return new B1; // or wherever the B1 object comes from...
}

A2.h

#include "A2.h"
#include "B2.h" // <--

B* A2::f()
{
    return new B2; // or wherever the B2 object comes from...
}

B1.cpp

#include "B1.h"
#include "A1.h" // <--

A* B1::g()
{
    return new A1; // or wherever the A1 object comes from...
}

B2.cpp

#include "B2.h"
#include "A2.h" // <--

A* B2::g()
{
    return new A2; // or wherever the A2 object comes from...
}

这只是意味着在调用 f()g() 时,无论您在哪里依赖协方差,都必须使用类型转换。

【讨论】:

  • 你确定这行得通吗?在 A1.h 中,前向类 B1 并没有告诉 B1 是 B 的派生类。这是我的观点。
  • @user1899020:协变返回类型也不适用于所有编译器。
  • 协变返回类型在 VS2015 中被支持。但这一个更复杂。甚至不知道它是否是协变的。
  • 我目前放弃了一种协变返回类型。
猜你喜欢
  • 1970-01-01
  • 2014-05-17
  • 1970-01-01
  • 2014-01-31
  • 1970-01-01
  • 2013-03-28
  • 1970-01-01
  • 2013-12-16
  • 1970-01-01
相关资源
最近更新 更多