最近我的代码中有一些问题围绕着我现在所知的循环依赖.简而言之,有两个类,Player和Ball,它们都需要使用另一个类的信息.代码中的某些位置都会传递另一个引用(来自另一个包含两个.h文件的类).
在阅读完之后,我从每个文件中删除了#include.h文件并进行了前向声明.这解决了能够在彼此中声明类的问题,但是当我尝试访问对象的传递引用时,我现在留下了"不完整类型错误".似乎有一些类似的例子,虽然经常混合更复杂的代码,很难缩小到基础.
我已经用最简单的形式(基本上是一个骨架)重写了代码.
Ball.h:
class Player;
class Ball {
public:
Player& PlayerB;
float ballPosX = 800;
private:
};
Run Code Online (Sandbox Code Playgroud)
Player.h:
class Ball;
class Player {
public:
void doSomething(Ball& ball);
private:
};
Run Code Online (Sandbox Code Playgroud)
Player.cpp:
#include "Player.h"
void Player::doSomething(Ball& ball) {
ball.ballPosX += 10; // incomplete type error occurs here.
}
Run Code Online (Sandbox Code Playgroud)
任何帮助理解为什么会这样的情况将不胜感激:)
娜塔莉
我以为我很熟悉C语法,直到我尝试编译以下代码:
void f(int i; double x)
{
}
Run Code Online (Sandbox Code Playgroud)
我期望编译器跳闸,但确实如此,但我没有收到错误消息:
test.c:1:14: error: parameter ‘i’ has just a forward declaration
Run Code Online (Sandbox Code Playgroud)
然后我试过了
void fun(int i; i)
{
}
Run Code Online (Sandbox Code Playgroud)
失败了
test.c:1:17: error: expected declaration specifiers or ‘...’ before ‘i’
Run Code Online (Sandbox Code Playgroud)
最后
void fun(int i; int i)
{
}
Run Code Online (Sandbox Code Playgroud)
令我惊讶的是,这成功了!
我从未在现实世界的C代码中看到过这种语法.有什么用?
众所周知,使用前向声明比在头文件中使用#includes更好,但是管理前向声明的最佳方法是什么?
有一段时间,我手动向每个头文件添加该头文件所需的前向声明.但是,我最终得到了一堆头文件重复相同的六个左右的前向声明,这似乎是多余的,并且维护这些重复的列表变得有点单调乏味.
typedef的前向声明(例如struct SensorRecordId; typedef std::vector<SensorRecordId> SensorRecordIdList;)在多个头文件中复制也有点多.
因此,我创建了一个ProjectForwards.h包含所有前向声明的文件,并将其包含在需要的任何地方.起初,这似乎是个好主意 - 更不用说冗余了,而且更容易维护typedef.但是现在,由于大量使用ProjectForwards.h,每当我添加一个新类时,我都必须重建世界,这会减慢开发速度.
那么管理前向声明的最佳方法是什么?我应该咬紧牙关并在多个子系统中重复单独的前向声明吗?继续这种ProjectForwards.h方法?尝试拆分ProjectForwards.h成几个SubsystemForwards.h文件?我忽视了一些其他解决方案?
我有一些代码,我需要向前声明一个模板类(或者至少,前向声明会让事情变得更容易......).我写了一个我正在解决的问题的简化版本,所以我可以在这里显示它:
template<bool>
class MyTemplateClass;
int main( int argc, char* argv[] )
{
MyTemplateClass<false> myTemp; // error here
myTemp.GetTheValue();
return 0;
}
template<bool bShouldMult>
class MyTemplateClass
{
int m_myint;
float m_myfloat;
public:
MyTemplateClass() : m_myint(5), m_myfloat(3.0f) {}
float GetTheValue()
{
return m_myint * (bShouldMult ? m_myfloat : 1.0f);
}
};
Run Code Online (Sandbox Code Playgroud)
我在注释行中得到的错误是:
Error - implicit instantiation of undefined template 'MyTemplateClass<false>'
Run Code Online (Sandbox Code Playgroud)
我需要在MyTemplateClass的前向声明中包含哪些其他细节?由于错误不是来自下一行,我假设它不是由于该方法未定义的事实.我正在使用的编译器是LLVM/CLang,我正在Mac上编译.
我必须处理一个由许多模板化类组成的库,这些类当然都是在头文件中实现的.现在我正试图找到一种方法来减少无法忍受的长编译时间,因为我几乎必须在每个编译单元中包含整个库.
尽管有模板,使用前向声明是否可能?我正在尝试下面的例子中的一些东西,我尝试绕过它#include <vector>,作为一个例子,但它给了我一个链接器错误,因为push_back未定义.
#include <iostream>
namespace std {
template<class T>
class vector {
public:
void push_back(const T& t);
};
}
int main(int argc, char** argv) {
std::vector<int>* vec = new std::vector<int>();
vec->push_back(3);
delete vec;
return EXIT_SUCCESS;
}
$ g++ fwddecl.cpp
ccuqbCmp.o(.text+0x140): In function `main':
: undefined reference to `std::vector<int>::push_back(int const&)'
collect2: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)
我尝试了一次预编译的头文件但是根本没有改变编译时间(我确实确实加载了它们而不是真正的头文件).但是,如果你们都说预编译头应该是可行的方式,那么我将再试一次.
更新:有些人说,转发声明STL类是不值得的.我应该强调,vector上面的STL 只是一个例子.我并没有真正尝试向前声明STL类,但它是关于我必须使用的某些库的其他严格模板化的类.
更新2:有没有办法使上面的例子实际编译和链接正确?Logan建议使用-fno-implicit-templates并放在template class std::vector<int>某个地方,大概是一个.cpp可以编译的单独文件-fno-implicit-templates,但我仍然会遇到链接器错误.再次,我试图理解它是如何工作的,std::vector以便我可以将它应用于我实际使用的模板化类.
考虑用来解释一下这个典型的例子并不与前向声明做:
//in Handle.h file
class Body;
class Handle
{
public:
Handle();
~Handle() {delete impl_;}
//....
private:
Body *impl_;
};
//---------------------------------------
//in Handle.cpp file
#include "Handle.h"
class Body
{
//Non-trivial destructor here
public:
~Body () {//Do a lot of things...}
};
Handle::Handle () : impl_(new Body) {}
//---------------------------------------
//in Handle_user.cpp client code:
#include "Handle.h"
//... in some function...
{
Handle handleObj;
//Do smtg with handleObj...
//handleObj now reaches end-of-life, and BUM: Undefined behaviour
}
Run Code Online (Sandbox Code Playgroud)
我从标准中了解到这个案例正朝向UB,因为Body的析构函数是非常重要的.我想要了解的是这个的根本原因.
我的意思是,问题似乎是由Handle的dtor内联的事实"触发",因此编译器执行类似下面的"内联扩展"(这里几乎是伪代码).
inline Handle::~Handle()
{ …Run Code Online (Sandbox Code Playgroud) c++ destructor memory-management forward-declaration delete-operator
这个让我想到:
class X;
void foo(X* p)
{
delete p;
}
Run Code Online (Sandbox Code Playgroud)
delete p如果我们甚至不知道是否X有可见的析构函数,我们怎么可能呢?g ++ 4.5.1给出了三个警告:
Run Code Online (Sandbox Code Playgroud)warning: possible problem detected in invocation of delete operator: warning: 'p' has incomplete type warning: forward declaration of 'struct X'
然后它说:
注意:即使在定义类时声明析构函数也不会调用析构函数或特定于类的运算符delete.
哇......像g ++一样诊断这种情况需要编译器吗?还是未定义的行为?
c++ pointers forward-declaration delete-operator incomplete-type
我在Xcode 4上收到语义警告: *在这个函数之外,'struct sockaddr_in'的声明将不可见* 结构似乎在netinet/in.h中声明
该警告已在Reachability.h上标记,它是我从Apple示例下载的类.
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
typedef enum {
NotReachable = 0,
ReachableViaWiFi,
ReachableViaWWAN
} NetworkStatus;
#define kReachabilityChangedNotification @"kNetworkReachabilityChangedNotification"
@interface Reachability: NSObject
{
BOOL localWiFiRef;
SCNetworkReachabilityRef reachabilityRef;
}
//reachabilityWithHostName- Use to check the reachability of a particular host name.
+ (Reachability*) reachabilityWithHostName: (NSString*) hostName;
//reachabilityWithAddress- Use to check the reachability of a particular IP address.
+ (Reachability*) reachabilityWithAddress: (const struct sockaddr_in*) hostAddress;
//reachabilityForInternetConnection- checks whether the default route is available.
// Should be used by applications …Run Code Online (Sandbox Code Playgroud) 我刚接受采访.我被问到什么是"前瞻性声明".然后我被问到他们是否是与前瞻性声明有关的危险.
我无法回答第二个问题.在网上搜索没有显示任何有趣的结果.
那么,有人知道与使用前向声明有关的任何危险吗?
我目前想知道为什么在编译/链接小型C程序时我没有从GCC收到错误.
我在version.h以下字符串中声明:
const char* const VERSION;
Run Code Online (Sandbox Code Playgroud)
在version.c我已设置变量的初始化:
const char* const VERSION = "0.8 rev 213";
Run Code Online (Sandbox Code Playgroud)
没问题.我可以在程序的其余部分使用该字符串.
如果缺少c文件,则在编译/链接期间不会发生错误,但是当程序尝试访问变量时,程序会失败并且SIGSEGV(当然).
我设置变量的方法是VERSION正确的还是有更好的方法?或者在编译/链接期间是否有机会出错?
c global-variables definition header-files forward-declaration
c++ ×7
c ×2
templates ×2
clang ×1
class ×1
cocoa ×1
compilation ×1
definition ×1
destructor ×1
header-files ×1
linker ×1
macos ×1
parameters ×1
pointers ×1
reference ×1
syntax ×1