标题:关于头文件
只看楼主
humy
Rank: 2
等 级:论坛游民
帖 子:69
专家分:18
注 册:2012-7-23
结帖率:92.86%
已结贴  问题点数:20 回复次数:21 
关于头文件
1.书上说:设计头文件应使其能被多次包含在同一源文件中所以只有声明不定义 a. 那我如下写的类定义是不是错的?
#include <string>
#include <iostream>
using namespace std;
class screen{
public:
typedef string ::size_type index;
char get() const{ return contents[ cursor];}
inline char get(index ht,index wd) const;
index get_cursor() const;
screen(index hght, index wdth,const string & cntnts);
screen& move (index r,index c);
screen& set (char);
screen& display(ostream & os);
private :
string contents;//b. 是不是这些变量前都要在前面加上extern?如果这样是不是在cpp里还要把这部分写一遍啊,只是不加extern?
index cursor;
index height;
index width;
};
2.又有一章说:类定义放在.h里,成员函数定义一般放在同名的源文件里 a. 结合问题1,变量声明在头文件,这说函数定义在源文件。那变量定义在哪? b. 我们写main时包含了头文件,但没提 其同名源文件的事啊,编译器会自动去找? c. 我用vs2010 的添加选项:除了创建项里有头文件,还有一个创建类。那用这个创建类的选项和 书上所说,用头文件,源文件的方式有区别吗?有的话哪个好? d. 创建的头文件不在当前的工程下,要包含这个头文件仍是直接#include"screen"就好了? 谢谢
搜索更多相关主题的帖子: contents private display 
2012-08-22 14:46
lonmaor
Rank: 16Rank: 16Rank: 16Rank: 16
来 自:郑州
等 级:版主
威 望:75
帖 子:2637
专家分:6423
注 册:2007-11-27
得分:8 
a.可以。如果你的main.cpp文件中同样包含iostream和string头文件,不用担心重复定义。防止头文件被重复包含用的是宏命令:#ifndef/#endif
b. 不用加extern

a.类成员的变量定义和函数声明习惯性写在.h头文件中
b.main.cpp只要包含头文件就可以了。.h中的函数声明会自动到同名源文件中搜索函数定义
c.通过创建类选项,vs2010会自动创建.h和同名的.cpp文件。比较方便。
d.vs2010的项目有个include属性,表示编译的时候到这些目录中去搜索头文件,你只要添加了相应的路径就可以搜索到了。
一般编译器是通过环境变量INCLUDE来确定头文件的搜索路径,你可以进入命令行窗口后打set回车,可以看到当前的环境变量设置。

从不知道到知道,到知道自己不知道,成长的道路上脚步深深浅浅
2012-08-22 19:07
pangding
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:北京
等 级:贵宾
威 望:94
帖 子:6784
专家分:16751
注 册:2008-12-20
得分:12 
如 lonmaor 所说。防止头文件被重复包含一般是用条件预处理命令:
程序代码:
#ifndef XXX
#define XXX

/* content of this header */

#endif
这样第一次包含这个文件的时候,由于 XXX 没有其它文件定义,因而 if 和 endif 之间的内容会被执行,从而定义了 XXX,和头文件中本来需要定义的东西。
之后如果再包含这个头文件,由于 XXX 已经定义过了。会跳过 if 和 endif 之间的内容。相当于什么都没干。

但这依然不是万能的解决方法。因为如果一个变量既在 a.h 中有所定义,又在 b.h 中有所定义。那么最终会因为重复定义而产生错误。
所以一般只能用 exterm 声明变量,而在对应的 cpp 中定义。因为 cpp 不会被其它文件包含,从而保证变量最多被定义一次。
但使用这种技术不表代就不会出问题。比如如果设计不当,就可能会出现循环包含。比如 a.h 包含 b.h,而 b.h 又包含 a.h。这种情况也许比较少见,但工程复杂之后间接的循环包含却可能会出现。设计良好的头文件不应该出现任何循环包含。


你 b 问的那个问题,contents 其实只是类 screen 定义的一部分。并不是在单独定义变量,因此不用加 extern。而在 cpp 里,也只用包含这个头文件就可以了。不用再重新定义。
按道理来讲,类和变量一样,不应该定义在 .h 里。但实际上人们却不是这么做的。
对于一个变量来说,知道它的类型,编译器就等于知道了它的一切。但是对于类来说,编译器如果见不到它的完整定义,根本无法确定它的有关信息。比如应该为它分配几个字节的内存,或者它有哪些成员函数等等。
所以类必须得定义在头文件里,包含之后才能使用。不过这样的话 a.h 和 b.h 里都定义了同名的类,那就会出错。名空间的一个目的就是为了减少这种情况带来的冲突,但需要精心的设计。这和刚才说头文件的设计一样,问题没有万能的解决方法。

2.又有一章说:类定义放在.h里,成员函数定义一般放在同名的源文件里 a. 结合问题1,变量声明在头文件,这说函数定义在源文件。那变量定义在哪?
变量应该定义在 cpp 里。原因如上述。

b. 我们写main时包含了头文件,但没提 其同名源文件的事啊,编译器会自动去找?
只要编译能获得类型相关的全部信息,它就可以完成编译的工作。比如编译器只要知道 int func(int, int) 这个函数原型,就能正确处理传参数、正确处理返回值的问题。并生成正确的函数调用的代码。而不需要知道函数的定义。
比如你包含的系统库中的函数,有些就只有函数原型,没有定义。但这不影响编译的过程。
所有源文件都编译完成之后,还有链接的的工作。它负责把函数调用和函数的定义联系在一起。它会在一些指定的地方查找函数的定义,比如系统库文件的位置和当前工程指定的位置。如果这时链接器发现有该找的函数定义找不到,就会产生链接错误。这一般和语法错误有明显区别。
头文件和源文件同不同名,其实对于编译和链接来说都无关紧要。一个头文件中声明的函数,即使定义在数个源文件里也是可以的。但是人喜欢看同名的。因为之后找起来方便,逻辑关系也更清楚些。

c. 我用vs2010 的添加选项:除了创建项里有头文件,还有一个创建类。那用这个创建类的选项和 书上所说,用头文件,源文件的方式有区别吗?有的话哪个好?
自动创建和手动创建没有本质区别。但前者由于自动化,可以节约一些时间和精力。至于哪个好,是个人品味的问题。如果用得习惯,当然是自动化的方法好喽。

d. 创建的头文件不在当前的工程下,要包含这个头文件仍是直接#include"screen"就好了?
如果可以搜索得到,包含一下就行了。如果不能,讲按 2楼 讲的方法进行设置。


有些问题讲得稍微深了点。楼主尽量理解就好。


[ 本帖最后由 pangding 于 2012-8-22 22:09 编辑 ]
2012-08-22 22:00
有容就大
Rank: 16Rank: 16Rank: 16Rank: 16
来 自:东土大唐
等 级:版主
威 望:74
帖 子:9048
专家分:14309
注 册:2011-11-11
得分:0 
说的好 帮顶

梅尚程荀
马谭杨奚







                                                       
2012-08-22 22:58
humy
Rank: 2
等 级:论坛游民
帖 子:69
专家分:18
注 册:2012-7-23
得分:0 
回复 3楼 pangding
非常感谢两位的回答,很满意。但恕我才疏学浅,再追问几句:

1.书上在说完  以防重复定义所以头文件只声明   后,就讲了条件预处理命令。两者是   并列关系  即  是两种处理 多次定义  的方法?
2.“contents 其实只是类 screen 定义的一部分。并不是在单独定义变量,因此不用加 extern。而在 cpp 里,也只用包含这个头文件就可以了。不用再重新定义。”是说类定义里的对变量的操作都不是定义?是声明?那怎么还会“因为如果一个变量既在 a.h 中有所定义,又在 b.h 中有所定义。那么最终会因为重复定义而产生错误。所以一般只能用 exterm 声明变量,而在对应的 cpp 中定义。”?   没理解,所以这两句意思感觉矛盾。。。   “变量应该定义在 cpp 里。”到底怎么定义呢?
3“.vs2010的项目有个include属性,表示编译的时候到这些目录中去搜索头文件,你只要添加了相应的路径就可以搜索到了”如图: 是这个吗?包含目录后面的内容是:$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include;没看懂。。。这些是路径?
4.“你可以进入命令行窗口后打set回车,可以看到当前的环境变量设置。”  请问命令行窗口指什么?是图中的吗?怎么没呢?
5“main.cpp只要包含头文件就可以了。.h中的函数声明会自动到同名源文件中搜索函数定义”和“它负责把函数调用和函数的定义联系在一起。它会在一些指定的地方查找函数的定义,比如系统库文件的位置和当前工程指定的位置。”说的不一样,还是后者对吧。但“一些指定的地方”是?我是可以随便在一个工程里建一个定义函数的cpp吗?只要有,他就能找到是吗?
2012-08-23 10:20
lonmaor
Rank: 16Rank: 16Rank: 16Rank: 16
来 自:郑州
等 级:版主
威 望:75
帖 子:2637
专家分:6423
注 册:2007-11-27
得分:0 
1.书上在说完  以防重复定义所以头文件只声明   后,就讲了条件预处理命令。两者是   并列关系  即  是两种处理 多次定义  的方法?
一般似乎只在.h文件中添加#ifndef/#endif预定义语句,同名的.cpp中并不包含#ifndef/#endif语句。变量定义和函数声明写在头文件里,这是个好习惯。

 3“.vs2010的项目有个include属性,表示编译的时候到这些目录中去搜索头文件,你只要添加了相应的路径就可以搜索到了”如图: 是这个吗?包含目录后面的内容是:$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include;没看懂。。。这些是路径?
诸如 $(VCInstallDir)、$(WindowsSdkDir)都是一些宏定义变量,在用到的时候前者会被展开为类似 c:/program files/visual studio/vc10/的绝对路径。

 4.“你可以进入命令行窗口后打set回车,可以看到当前的环境变量设置。”  请问命令行窗口指什么?是图中的吗?怎么没呢?
呃,这里命令行方式不是指vs的命令行方式,是windows程序的附件中的命令提示符,用win+R打开运行窗口,输入cmd回车也是一样的。

5“main.cpp只要包含头文件就可以了。.h中的函数声明会自动到同名源文件中搜索函数定义”和“它负责把函数调用和函数的定义联系在一起。它会在一些指定的地方查找函数的定义,比如系统库文件的位置和当前工程指定的位置。”说的不一样,还是后者对吧。但“一些指定的地方”是?我是可以随便在一个工程里建一个定义函数的cpp吗?只要有,他就能找到是吗?
这个暂以p版的解答为准吧。你说的方法可以试试,即使可以用,但没有这个习惯。

从不知道到知道,到知道自己不知道,成长的道路上脚步深深浅浅
2012-08-23 11:25
humy
Rank: 2
等 级:论坛游民
帖 子:69
专家分:18
注 册:2012-7-23
得分:0 
回复 6楼 lonmaor
1.
添加#ifndef/#endif预定义语句,感觉函数定义什么的就可以直接写在头文件里也没事了吧,那为何还要建源文件?



4.
这里命令行方式不是指vs的命令行方式,是windows程序的附件中的命令提示符,用win+R打开运行窗口,输入cmd回车也是一样的。
吧?

非常感谢。
2012-08-23 13:47
humy
Rank: 2
等 级:论坛游民
帖 子:69
专家分:18
注 册:2012-7-23
得分:0 
还有“比如如果设计不当,就可能会出现循环包含。比如 a.h 包含 b.h,而 b.h 又包含 a.h”
书上有个例子:Message 类里有一个set<Folder*>保存message所在所有文件夹的指针。而类Folder里有一个set<Message*>保存文件夹里所有文件的指针
如Folder类定义前写一个Message的声明,不就解决了循环包含吗?
程序代码:
#include<set>
#include<string>
class Message
class Folder
{//...}
2012-08-23 14:24
lonmaor
Rank: 16Rank: 16Rank: 16Rank: 16
来 自:郑州
等 级:版主
威 望:75
帖 子:2637
专家分:6423
注 册:2007-11-27
得分:0 
嵌套包含的实例我也没用过。
大致是类似于
程序代码:
class a;
class b
{
   ...
   a* pa;
}
class a
{
   ...
   b* pb;
}


的东西

从不知道到知道,到知道自己不知道,成长的道路上脚步深深浅浅
2012-08-23 14:55
humy
Rank: 2
等 级:论坛游民
帖 子:69
专家分:18
注 册:2012-7-23
得分:0 
程序代码:
Folder& Folder::operator=(const Folder&f){
    if(& f!=this){
        remove_Fl_from_Msg();
        messages=f.messages;
        put_Fl_in_Msg(messages);
     }
    return *this;
}
还有为什么返回值类型Folder&前没有Folder::
2012-08-23 14:56



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-379925-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.509628 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved