在 C++ 中,是否可以将类声明为从另一个类继承?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2159390/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
In C++, is it possible to forward declare a class as inheriting from another class?
提问by anon
I know that I can do:
我知道我可以做到:
class Foo;
but can I forward declare a class as inheriting from another, like:
但是我可以转发声明一个类从另一个类继承,例如:
class Bar {};
class Foo: public Bar;
An example use case would be co-variant reference return types.
一个示例用例是协变引用返回类型。
// somewhere.h
class RA {}
class RB : public RA {}
... and then in another header that doesn't include somewhere.h
...然后在另一个不包含某处的标头中.h
// other.h
class RA;
class A {
public:
virtual RA* Foo(); // this only needs the forward deceleration
}
class RB : public RA; // invalid but...
class B {
public:
virtual RB* Foo(); //
}
The only information the compiler shouldneed to process the declaration of RB* B:Foo()
is that RB
has RA
as a public base class. Now clearly you would need somewhere.h if you intend to do any sort of dereferencing of the return values from Foo
. However, if some clients never calls Foo
, then there is no reason for them to include somewhere.h which might significantly speed compilation.
唯一的信息编译器应该需要处理的声明,RB* B:Foo()
即RB
具有RA
作为公共基类。现在很明显,如果您打算对来自Foo
. 但是,如果某些客户端从不调用Foo
,则他们没有理由包含可能会显着加快编译速度的 something.h。
采纳答案by Joe
A forward declaration is only really useful for telling the compiler that a class with that name does exist and will be declared and defined elsewhere. You can't use it in any case where the compiler needs contextual information about the class, nor is it of any use to the compiler to tell it only a little bit about the class. (Generally, you can only use the forward declaration when referring to that class without other context, e.g. as a parameter or return value.)
前向声明仅在告诉编译器具有该名称的类确实存在并将在其他地方声明和定义时才真正有用。在编译器需要有关类的上下文信息的任何情况下,您都不能使用它,编译器也不能仅告诉它有关类的一点点信息。(通常,在没有其他上下文的情况下引用该类时,您只能使用前向声明,例如作为参数或返回值。)
Thus, you can't forward declare Bar in any scenario where you then use it to help declare Foo, and it flat-out doesn't make sense to have a forward declaration that includes the base class -- what does that tell you besides nothing?
因此,您不能在任何使用它来帮助声明 Foo 的情况下转发声明 Bar,并且拥有包含基类的转发声明完全没有意义——除了告诉你什么之外没有?
回答by Jonathan M Davis
Forward declarations are declarations, not definitions. So, anything that requires the declaration of a class (like pointers to that class) need only the forward declaration. However, anything that would require the definition - i.e. would need to know the actual structure of the class - would not work with just the forward declaration.
前向声明是声明,而不是定义。因此,任何需要声明类(如指向该类的指针)的东西都只需要前向声明。然而,任何需要定义的东西——即需要知道类的实际结构——都不能只使用前向声明。
Derived classes definitely need to know the structure of their parent, not just that the parent exists, so a forward declaration would be insufficient.
派生类肯定需要知道其父类的结构,而不仅仅是父类存在,因此前向声明是不够的。
回答by user1332054
No it is not possible to forward declare inheritance, even if you are only dealing with pointers. When dealing with conversions between pointers, sometimes the compiler has to know the details of the class to do the conversion correctly. This is the case with multiple inheritance. (You could special case some parts parts of the hierarchy that only use single inheritance, but that isn't part of the language.)
不,即使您只处理指针,也不可能转发声明继承。在处理指针之间的转换时,有时编译器必须知道类的详细信息才能正确进行转换。多重继承就是这种情况。(您可以将层次结构中仅使用单一继承的某些部分作为特殊情况,但这不是语言的一部分。)
Consider the following trivial case:
考虑以下简单案例:
#include <stdio.h>
class A { int x; };
class B { int y; };
class C: public A, public B { int z; };
void main()
{
C c; A *pa = &c; B *pb = &c; C *pc = &c;
printf("A: %p, B: %p, C: %p\n", pa, pb, pc);
}
The output I received (using 32 bit visual studio 2010), is:
我收到的输出(使用 32 位 Visual Studio 2010)是:
A: 0018F748, B: 0018F74C, C: 0018F748
So for multiple inheritance, when converting between related pointers, the compiler has to insert some pointer arithmetic to get the conversions right.
所以对于多重继承,当在相关指针之间进行转换时,编译器必须插入一些指针算法来获得正确的转换。
This is why, even if you are dealing only with pointers, you can't forward declare inheritance.
这就是为什么,即使您只处理指针,也不能转发声明继承。
As for why it would be useful, it would improve compile times when you do want to make use of co-variant return types instead of using casts. For example this will not compile:
至于它为什么有用,当您确实想要使用协变返回类型而不是使用强制转换时,它会缩短编译时间。例如,这不会编译:
class RA;
class A { public: virtual RA *fooRet(); };
class RB;
class B : public A { public: virtual RB *fooRet(); };
But this will:
但这将:
class RA;
class A { public: virtual RA *fooRet(); };
class RA { int x; };
class RB : public RA{ int y; };
class B : public A { public: virtual RB *fooRet(); };
This is useful when you have objects of type B (not pointers or references). In this case the compiler is smart enough to use a direct function call, and you can use the return type of RB* directly without casting. In this case, usually I go ahead and make the return type RA * and do a static cast on the return value.
当您拥有 B 类型的对象(不是指针或引用)时,这很有用。在这种情况下,编译器足够聪明,可以使用直接函数调用,并且您可以直接使用 RB* 的返回类型而无需强制转换。在这种情况下,通常我会继续将返回类型设为 RA * 并对返回值进行静态转换。
回答by Elliott
All you needed to do was declare RB
without the : public RA
(oh, and also add ;
to the end of your class definitions):
您需要做的就是在RB
没有: public RA
(哦,并且还添加;
到类定义的末尾)进行声明:
class RA;
class A {
public:
virtual RA* Foo();
};
class RB;
class B {
public:
virtual RB* Foo();
};
// client includes somewhere.h
class RA {};
class RB : public RA {};
int main ()
{
return 0;
}
However, this doesn't solve the specific problem described nicely in the answer by user1332054.
但是,这并不能解决 user1332054 在回答中很好地描述的具体问题。
Some of the other answers appear to show some misconceptions that I'd like to dispel:
其他一些答案似乎显示了一些我想消除的误解:
Forward declaring isuseful evenwhen when we know that the definition is not likely to be included. This allows us to do a lot of type-deduction in our libraries that make them compatible with many other established libraries without including them. Including libraries unnecessarily leads to too many nested includes that can explode the compile time. It's good practice to make your code compatible when appropriate, and to include as little as possible.
即使当我们知道不太可能包含定义时,前向声明也很有用。这允许我们在我们的库中进行大量类型推导,使它们与许多其他已建立的库兼容,而无需包含它们。不必要地包含库会导致过多的嵌套包含,这会导致编译时间的延长。在适当的时候使您的代码兼容并尽可能少地包含是一种很好的做法。
Typically you candefine a class with pointers to classes that have only been declared and not defined. Example:
通常,您可以使用指向仅声明而未定义的类的指针来定义类。例子:
struct B;
struct A
{
B * b_;
B * foo ()
{
return b_;
}
B & foo (B * b)
{
return *b;
}
};
int main ()
{
return 0;
}
The above compiles fine, because the compiler doesn't need to know anything about B.
上面的编译很好,因为编译器不需要知道关于 B 的任何信息。
An example where it might be a bit harder to realise that the compiler needs more information:
意识到编译器需要更多信息可能有点困难的示例:
struct B;
struct A
{
B * foo ()
{
return new B;
}
};
The above problem is because new B
invokes the B::B()
constructor which hasn't been defined yet. Also:
上面的问题是因为new B
调用了B::B()
尚未定义的构造函数。还:
struct B;
struct A
{
void foo (B b) {}
};
Here foo
must call the copy constructor for b
, which also hasn't been defined yet. Lastly:
这里foo
必须为 调用复制构造函数b
,它也尚未定义。最后:
struct B;
struct A
{
B b;
};
Here we have implicitly defined A
with the default constructor, which calls the default constructor of call its members,b
, which hasn't been defined yet. I think' you get the point.
这里我们隐式定义A
了默认构造函数,它调用了 call 其成员的默认构造函数,b
尚未定义。我认为你说对了。
So, in reference to the more general problem, described by user1332054, I honestly don't understand why it's not possible to use pointers to undefined classed in an inherited virtual function.
因此,关于 user1332054 描述的更一般的问题,老实说,我不明白为什么不能使用指向继承的虚函数中未定义类的指针。
More broadly though, I think that you're making it more difficult for yourself by definingyour classes instead of only declaringthem. Here's an example where you get to DoCleverStuff
with your classes in your library before you've defined any of your classes at all:
更广泛地说,我认为你通过定义你的类而不是仅仅声明它们会让你自己变得更加困难。这是一个示例,您可以DoCleverStuff
在定义任何类之前使用库中的类:
// Just declare
class RA;
class RB;
class A;
class B;
// We'll need some type_traits, so we'll define them:
template <class T>
struct is_type_A
{
static constexpr bool value = false;
};
template <>
struct is_type_A <A>
{
static constexpr bool value = true;
};
template <class T>
struct is_type_B
{
static constexpr bool value = false;
};
template <>
struct is_type_B <B>
{
static constexpr bool value = true;
};
#include <type_traits>
// With forward declarations, templates and type_traits now we don't
// need the class definitions to prepare useful code:
template<class T>
typename std::enable_if<is_type_A<T>::value, RA *>::type
DoCleverStuff (T & t)
{
// specific to A
return t.fooRet();
}
template<class T>
typename std::enable_if<is_type_B<T>::value, RB *>::type
DoCleverStuff (T & t)
{
// specific to B
return t.fooRet();
}
// At some point the user *does* the include:
class RA
{
int x;
};
class RB : public RA
{
int y;
};
class A
{
public:
virtual RA * fooRet()
{
return new RA;
}
};
class B : public A
{
public:
virtual RB * fooRet()
{
return new RB;
}
};
int main ()
{
// example calls:
A a;
RA * ra = DoCleverStuff(a);
B b;
RB * rb = DoCleverStuff(b);
delete ra;
delete rb;
return 0;
}
回答by Andrew Aylett
I don't think it's useful. Consider: you have defined a class, Bar:
我不认为它有用。考虑:您已经定义了一个类 Bar:
class Bar {
public:
void frob();
};
Now you declare a class Foo:
现在你声明一个类 Foo:
class Foo;
All you can do with Foo is construct a pointer to it. Now, suppose you add the information that Foo
is derived from Bar
:
你可以用 Foo 做的就是构造一个指向它的指针。现在,假设您添加了Foo
来自Bar
以下内容的信息:
class Foo: public Bar;
What can you now do that you couldn't do before? I think that all you can do is accept a pointer to Foo
and cast it into a pointer to Bar
, then use that pointer.
你现在可以做以前不能做的事情?我认为您所能做的就是接受指向的指针Foo
并将其转换为指向 的指针Bar
,然后使用该指针。
void frob(Foo* f) {
Bar *b = (Bar)f;
b->frob();
}
However, you must have generated the pointer elsewhere, so you could have just accepted a pointer to Bar
instead.
但是,您必须在其他地方生成了指针,因此您可以只接受一个指向的指针Bar
。
void frob(Bar* b) {
b->frob();
}