在C++中使用前向声明的错误
我想在C++中编写一个简单的程序,我有2个类,并且都可以访问对方的函数。这是我最后想要做的事情的一个简化,那就是创建一个包含棋类和棋子类的游戏。我可以在java中做到这一点,但我现在遇到了一些问题,试图在C++中做同样的事情。我的代码如下:在C++中使用前向声明的错误
#include <iostream>
class B;
class A {
public:
void sync(B obj){
b = obj;
}
void print(){
std::cout << "Hello from class A" << std::endl;
}
void printBoth(){
std::cout << "Hello from class A" << std::endl;
b.print();
}
private:
B b;
};
class B {
public:
B(A obj){
a = obj;
}
void print(){
std::cout << "Hello from class B" << std::endl;
}
void printBoth(){
std::cout << "Hello from class B" << std::endl;
a.print();
}
private:
A a;
};
int main(){
A a;
B b(a);
a.sync(b);
a.printBoth();
std::cout << std::endl;
b.printBoth();
return 0;
}
当我试着使用g++ example.cpp
编译此,我收到5个错误:
example.cpp:20:5: error: field ‘b’ has incomplete type
B b;
^
example.cpp: In member function ‘void A::sync(B)’:
example.cpp:7:8: error: ‘obj’ has incomplete type
void sync(B obj){
^
example.cpp:3:7: error: forward declaration of ‘class B’
class B;
^
example.cpp:8:4: error: ‘b’ was not declared in this scope
b = obj;
^
example.cpp: In member function ‘void A::printBoth()’:
example.cpp:17:4: error: ‘b’ was not declared in this scope
b.print();
^
我从来没有使用前置声明带班,所以我道歉,如果我的思念显而易见的东西。任何帮助表示赞赏,我提前感谢你。
您的代码有两个问题。
让我们来看第一个问题。
下面是两个方法相互调用的例子。相当无意义,并导致无限循环,但是这显示了前向声明的一般原则,您可以使用它作为模板来修复您所显示的代码。
class A {
public:
void foo();
};
class B {
public:
void foo();
};
void A::foo()
{
B b;
b.foo();
}
void B::foo()
{
A a;
a.foo();
};
现在,你的代码的第二个问题:
书面,类A包含B类,它包含A类的实例,它包含A类的实例,实例,其包含B类的一个实例,直到宇宙在一个巨大的黑洞中内爆。只要说,这不会编译。
C++对象与Java对象有根本的不同。他们以完全不同的方式工作。你可以用Java来做这件事,因为这个肮脏的小秘密就是Java中的所有对象都是真正的海市蜃楼,它们实际上是引用计数的指向实际对象的指针。在C++中,这样做的最接近的将是一个std::shared_ptr
申报对象:
#include <memory>
class A;
class B;
class A {
// ...
private:
std::shared_ptr<B> b;
};
class B {
// ...
private:
std:shared_ptr<A> a;
};
,这将是与你的代码所示的第二个问题。一般来说,在学习C++对象时,你几乎忘记了所有关于Java对象的知识。它们有着根本的不同,并且试图在你已经了解的Java对象和C++对象之间持续地绘制类比,只会导致混淆。
谢谢你的帮助。我将尽力在将来关注java和C++之间的差异。我能够用一个传递给每个类的对象实际上使用第三个类来修复它,但我也会尝试这个。在更复杂的情况下,我的解决方案可能不太可取,但它在这里起作用。 – acn3
你总是可以尝试重新设计,即:
#include <iostream>
#include "A.hpp"
int main(){
A a;
B b(a);
a.sync(b);
a.printBoth();
std::cout << std::endl;
b.printBoth();
return 0;
}
B.hpp:
class A;
class B {
public:
B() {}
B(A obj);
void print();
void printBoth();
private:
A* a;
};
B.cpp:
#include <iostream>
#include "A.hpp"
B::B(A obj){
a = new A(obj);
}
void B::print(){
std::cout << "Hello from class B" << std::endl;
}
void B::printBoth(){
std::cout << "Hello from class B" << std::endl;
a->print();
}
A.hpp:
#include "B.hpp"
class A {
public:
void sync(B obj);
void print();
void printBoth();
private:
B b;
};
A.cpp:
#include <iostream>
#include "A.hpp"
void A::sync(B obj){
b = obj;
}
void A::print(){
std::cout << "Hello from class A" << std::endl;
}
void A::printBoth(){
std::cout << "Hello from class A" << std::endl;
b.print();
}
系绳向前声明,考虑一下:'A'有一个正式成员'B'。因此,如果没有构建“B”,那么'A'就不可能存在(不知何故,稍后会有更多介绍)。现在,'B'有一个构造函数,它将一个完整的值'A'作为唯一参数。因此,只有存在“A”时才能存在“B”,直到存在“B”时才能存在“A”。简而言之,转发声明不是问题。设计本身被打破。我认为很快就会有一些*参考文献*,因为无论您是否知道,Java都是这样管理的。 – WhozCraig
您应该使用'inheritance'而不是'aggregation'。 – has
当正向声明时应该提供指针,以便编译器不应该计算B对象的大小。在你的示例中,编译器需要计算A中B对象的大小,编译器应该如何知道这一点? – user3655463