QQ登录

只需一步,快速开始

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 1448|回复: 15

[C++]我遇到一个大麻烦…… :-(

[复制链接]
发表于 2004-7-8 03:35:01 | 显示全部楼层 |阅读模式
我的程序里有如下的代码:

[code:1]
//*****id.h*****/


//#include "list.h"         <--------注意这行

class Id{
public:
    Id();

    ~Id();

public:
    //static List* list;         <--------注意这行
    int id;
};

[/code:1]
[code:1]
//*****id.cpp*****/


#include "id.h"

//List* Id::list=NULL;         <--------注意这行

Id::Id()
{
  id=-1;
   
//  list->add(this);         <--------注意这行
}


Id::~Id()
{
}

[/code:1]
[code:1]
//*****list.h*****/


#include <stddef.h>

#include "id.h"         <--------注意这行
#include "list_node.h"

class List{
public:
    List();

    ~List();
    Id* find(int _id);
    void add(Id* _obj);
    void del(int _id);
//    void clear(int _id);
                                                                                                                             
protected:
    List_node objs[10];
    List_node* bobjs[10];
    int rw2;
    int rw1;
};

[/code:1]
[code:1]
//*****list.cpp*****/


#include "list.h"
                                                                                                                             
List::List()
{
  for( int i=0;i<10;++i ){
    objs[i].id=i;
    bobjs[i]=NULL;
  }
  rw1=0;
  rw2=-1;
}


List::~List()
{
}




/*!
    \fn List::find(int _id)
*/
Id* List::find(int _id)
{
  if (objs[_id].obj!=NULL){
    return objs[_id].obj;
  }
  else{
    return NULL;
  }
}
                                                                                                                             
                                                                                                                             
/*!
    \fn List::add(Id* _obj)
*/
void List::add(Id* _obj)
{
  if ( rw2>=0 ){
    bobjs[rw2]->obj=_obj;
    bobjs[rw2]->obj->id=bobjs[rw2]->id;
                                                                                                                             
    --rw2;
  }
  else{
    if ( rw1<=10-1 ){
      objs[rw1].obj=_obj;
      objs[rw1].obj->id=objs[rw1].id;
      ++rw1;
    }
    else{
      //new page
    }
  }
}
                                                                                                                             
                                                                                                                             
/*!
    \fn List::del(int _id)
*/
void List::del(int _id)
{
  if ( objs[_id].obj!=NULL ){
    delete objs[_id].obj;
    objs[_id].obj=NULL;
    bobjs[++rw2]=&objs[_id];
  }
}

[/code:1]

出错信息如下:
……
In file included from /home/sjinny/Prj/idtest/src/id.h:23,
*from /home/sjinny/Prj/idtest/src/id.cpp:20:
*/home/sjinny/Prj/idtest/src/list.h:37: error: syntax error before `*' token
*/home/sjinny/Prj/idtest/src/list.h:38: error: `Id' was not declared in this scope
*/home/sjinny/Prj/idtest/src/list.h:38: error: `_obj' was not declared in this scope
*/home/sjinny/Prj/idtest/src/list.h:38: error: invalid data member initialization
*/home/sjinny/Prj/idtest/src/list.h:38: error: (use `=' to initialize static data members)
*/home/sjinny/Prj/idtest/src/list.h:38: error: variable or field `add' declared void
……
In file included from /home/sjinny/Prj/idtest/src/id.h:23,
*from /home/sjinny/Prj/idtest/src/obj1.h:23,
*from /home/sjinny/Prj/idtest/src/obj1.cpp:20:
*/home/sjinny/Prj/idtest/src/list.h:37: error: syntax error before `*' token
*/home/sjinny/Prj/idtest/src/list.h:38: error: `Id' was not declared in this scope
*/home/sjinny/Prj/idtest/src/list.h:38: error: `_obj' was not declared in this scope
*/home/sjinny/Prj/idtest/src/list.h:38: error: invalid data member initialization
*/home/sjinny/Prj/idtest/src/list.h:38: error: (use `=' to initialize static data members)
*/home/sjinny/Prj/idtest/src/list.h:38: error: variable or field `add' declared void
……
In file included from /home/sjinny/Prj/idtest/src/id.h:23,
*from /home/sjinny/Prj/idtest/src/obj1.h:23,
*from /home/sjinny/Prj/idtest/src/obj2.h:23,
*from /home/sjinny/Prj/idtest/src/obj2.cpp:20:
*/home/sjinny/Prj/idtest/src/list.h:37: error: syntax error before `*' token
*/home/sjinny/Prj/idtest/src/list.h:38: error: `Id' was not declared in this scope
*/home/sjinny/Prj/idtest/src/list.h:38: error: `_obj' was not declared in this scope
*/home/sjinny/Prj/idtest/src/list.h:38: error: invalid data member initialization
*/home/sjinny/Prj/idtest/src/list.h:38: error: (use `=' to initialize static data members)
*/home/sjinny/Prj/idtest/src/list.h:38: error: variable or field `add' declared void
……



在id.h中,以及id.cpp中,有几行被注释掉了,如果取消这几个注释,那么编译时就会抱错,反复实验,发现头文件似乎不能被互相引用……即:
[code:1]
对于两个头文件:foo1.h和foo2.h
它们不能互相include
比如foo1.h中#include "foo2.h",同时foo2.h中又#include "foo1.h",编译时就会抱错。
[/code:1]
我试了一下,出错信息如下:
[sjinny@localhost t2]$ make
g++ -o foo foo.cpp
In file included from foo2.h:1,
                 from foo1.h:1,
                 from foo2.h:1,
                 from foo1.h:1,
                 from foo2.h:1,
                 from foo1.h:1,
                 from foo2.h:1,
                 from foo1.h:1,
                 from foo2.h:1,
                 from foo1.h:1,
                 from foo2.h:1,
同样的信息还有很多行……
                 from foo1.h:1,
                 from foo.cpp:1:
foo1.h:1:10: #include nested too deeply
make: *** [foo] 错误 1



不知道有什么好办法……
 楼主| 发表于 2004-7-8 03:49:52 | 显示全部楼层
我原来的想法是这样的:

网游中,每一个人物、物品都要有一个唯一的id,这个id一要用来作为这些对象唯一的标识,二要用来寻找这些对象(根据id找到对象,并返回引用或地址)。因为对象太多时到八叉树里去查找太费时间,所以我就想用一个hash表来管理这些有id的对象,使用它们的id作为数组下标,对应的数组元素是指向对应对象的指针,当然因为对象太多,所以会构建多个同样长度的hash表,所以实际使用的id可能是  “hash表的索引.hash表内的数组下标”这种形式。
但是因为这些对象所属的类一般都是由id类派生而来的,我希望所有从id类派生而来的类在实例化时能自动把自己“注册”到hash表中,这样我就想,如果在Id()中执行类似于  Id::list->add(this);  这样的语句,那么应该就可以实现自动“注册”了吧~
但是要这么做,我就得在id.h里include "list.h";而list.h也要include "id.h"因为List的实例里会有id*这样的指针还需要操作id类(及其派生类)的实例……这样就会有头文件的互相引用,于是就出错了……
本来还有个疑问:
在实例化一个派生类时,最先被执行的是否是最根本的父类的构建函数?在这个父类的构建函数里调用this指针得到的是否就是最终这个派生类的实例的地址?……但是现在编译不了,也就没法去做这些实验了……
回复

使用道具 举报

发表于 2004-7-8 08:35:16 | 显示全部楼层
从上面来看,你需要list的动作,id的数据类型,那你可以再写个类action.cpp,把你要做的regist之类的动作提取出来放到这个类里,头文件里包含id的.h,cpp里包含list的.h,这个类不论是被id使用还是被list使用都应该是安全的。
回复

使用道具 举报

发表于 2004-7-8 08:36:10 | 显示全部楼层
C++里的include预编译就是将头文件简单地copy到包含文件中,你这样相互引用就会造成无限循环。我想应该将公用的部份提出来放在一起(比如你的我觉得可以将id与list放在一起),因为以c++中的头文件实际上体现了面向对象中接口的概念,提取哪些公用部份要认真计划一下。
回复

使用道具 举报

发表于 2004-7-8 18:53:46 | 显示全部楼层
你用export试一下,例如 export List;
这样就不用包含list的头文件list.h
即#include "list.h" 替换成 export List
回复

使用道具 举报

 楼主| 发表于 2004-7-8 18:55:14 | 显示全部楼层
flyaway, 我最初的目的就是“自动注册”,如果把注册过程独立出来,那么还得再用factory之类的从派生类的外部进行注册了……好象不够完美~

sagaeon, 我猜测恐怕不完全是头文件的问题。因为:
完成对id的定义 需要 list的完整定义, 而完成list的定义 又需要 id的完整定义 ……这样本身就会无限循环下去……就好象我们对一个人解释“硬币的反面”的概念:
“硬币的反面就是与正面相对的那一面”
“那么正面又是什么呢?”
“正面就是与反面相对的那一面呀”
“……”

但是想想,有这么一种司空见惯的情况:
[code:1]
Class A
{
  Public:
    A* next;
};
[/code:1]
这种结构在链表里很常见,但是似乎也是有疑问的:
完成对A的定义  需要  完成对A* 的定义,完成对A*的定义 又需要完成对A的定义……
这样似乎也会无限循环下去……但是编译器从不会为这事报错,而且程序也运行地很好……

所以……我还是先试试再说吧……
回复

使用道具 举报

 楼主| 发表于 2004-7-8 18:56:27 | 显示全部楼层
[quote:d1e505578a="dxz"]你用export试一下,例如 export List;
这样就不用包含list的头文件list.h
即#include "list.h" 替换成 export List[/quote]

export是什么??   
怎么我对这个关键字一点映象都没有……   
回复

使用道具 举报

发表于 2004-7-8 19:59:02 | 显示全部楼层
似乎是extern   
回复

使用道具 举报

发表于 2004-7-8 20:04:49 | 显示全部楼层
用#ifdef这样的预处理指令不知能否达到你的要求? :-)
回复

使用道具 举报

 楼主| 发表于 2004-7-8 20:58:03 | 显示全部楼层
idtest程序里我用了#ifdef了啊,只是这只能防止重复include。不过我后来那个include测试倒是忘了用了……  -_-#
回复

使用道具 举报

发表于 2004-7-9 16:04:36 | 显示全部楼层
sjinny对编译方面不是很熟啊?那个问题不就是
Class A
{
  Public:
    A* next;
}; 这个定义正确

Class A
{
  Public:
    A next;
}; 这个定义错误
这么回事情么?
回复

使用道具 举报

 楼主| 发表于 2004-7-9 17:52:21 | 显示全部楼层
大家看看这段代码:
[code:1]
class B;        <------注意这一行
class A
{
public:
  B* m;
};

class B
{
public:
  A* n;
};

int main(int argc, char* argv[])
{
  return 0;
}
[/code:1]
如果把第一行注释掉,那么就会编译出错:
[sjinny@localhost t3]$ make
g++ -o main main.cpp
main.cpp:5: error: syntax error before `*' token
make: *** [main] 错误 1
[sjinny@localhost t3]$

但是一旦有了那第一行,就能编译成功了,没有错误,也没有警告……           

再看这些:
[code:1]
class B;
class A
{
public:
  B m;       <------注意这一行,原来声明的是指针,现在是一个对象
};

class B
{
public:
  A* n;
};

int main(int argc, char* argv[])
{
  return 0;
}
[/code:1]
这时再编译就会出错:
[sjinny@localhost t3]$ make
g++ -o main main.cpp
main.cpp:5: error: field `m' has incomplete type
make: *** [main] 错误 1
[sjinny@localhost t3]$

后来我把class A改成了这样:
[code:1]
class A
{
public:
  B* m;

  A x;
};
[/code:1]
编译结果也出错:
main.cpp:7: error: field `x' has incomplete type


这下我算有点明白了:
声明的指针变量,无论是什么类型的指针,它所占用的空间都是相同的(我也不太肯定,请大家确认一下),所以在声明指针变量时,只要有那个类型的声明就可以了,“class B;”这一行就告诉了编译器有个B这种类型,在对B进行详细定义之前(也就是在class A里),定义B*型的指针是可以的,但是定义B型的数据成员就不行了,因为此时B的定义还“不完整”……



我以前好象在别人的代码也见到过class xxxx;这种语句,当时怎么也看不明白是什么意思,今天拿来一试,还真灵验……     

不过谁知道具体的情况?我看书的时候好象没看到这种内容……
回复

使用道具 举报

发表于 2004-7-9 18:53:13 | 显示全部楼层
你所帖的代码中:
[code:1]
class B;
class A
{
public:
  B m;
};

class B
{
public:
  A* n;
};

int main(int argc, char* argv[])
{
  return 0;
}
[/code:1]

第一个“class B;”其实就是预声明,否则在class A中编译器无法识别B到底是何样东西。所起的作用跟声明函数原型的道理是一样的。通常将这些声明放到头文件*.h中,代码中只须包含这个头文件即可。

你在最前面的问题中主要是因为你没有很好地理解头文件的用法,所以会引导代码相互包含而导致重复定义的错误。
回复

使用道具 举报

 楼主| 发表于 2004-7-9 20:55:38 | 显示全部楼层
那么是不是应该专门做一个头文件,里面写上所有类的预声明,然后在所有类的头文件中都包含这个头文件?这样是不是会好点……
回复

使用道具 举报

发表于 2004-7-9 21:34:30 | 显示全部楼层
在规划一个多文件的项目时,只要将同一类或相似功能的代码放入一个头文件中,并写一个头文件列出该文件所有函数的声明、重要定义和全局变量声明(而不是将所有这些全放进一个头文件)。一般只在此文件及主文件中包含这个头文件;如果其它文件中的代码也用到些文件中所定义的资源(函数、宏、定义、全局变量等)才需要包含这个头文件。

你可以找一个软件源码看看具体实例,这样很容易就能够掌握。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

GMT+8, 2024-11-7 23:37 , Processed in 0.090575 second(s), 15 queries .

© 2021 Powered by Discuz! X3.5.

快速回复 返回顶部 返回列表