C的风格与技巧Word文档下载推荐.docx
- 文档编号:231949
- 上传时间:2023-04-28
- 格式:DOCX
- 页数:39
- 大小:30.71KB
C的风格与技巧Word文档下载推荐.docx
《C的风格与技巧Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《C的风格与技巧Word文档下载推荐.docx(39页珍藏版)》请在冰点文库上搜索。
请注意,这仅仅是一个常见问题与解答的列表。
它不能代替一本优秀教科书中那些经过精心挑选的范例与解释。
它也不能象一本参考手册或语言标准那样,提供详细和准确的说明。
有关C++的设计的问题,请参见《C++语言的设计和演变》(TheDesignandEvolutionofC++)。
关于C++语言与标准库的使用,请参见《C++程序设计语言》(TheC++ProgrammingLanguage)。
目录:
我如何写这个非常简单的程序?
为什么编译要花这么长的时间?
为什么一个空类的大小不为0?
我必须在类声明处赋予数据吗?
为什么成员函数默认不是virtual的?
为什么析构函数默认不是virtual的?
为什么不能有虚拟构造函数?
为什么重载在继承类中不工作?
我能够在构造函数中调用一个虚拟函数吗?
有没有“指定位置删除”(placementdelete)?
我能防止别人继承我自己的类吗?
为什么不能为模板参数定义约束(constraints)?
既然已经有了优秀的qsort()函数,为什么还需要一个sort()?
什么是函数对象(functionobject)?
我应该如何对付内存泄漏?
我为什么在捕获一个异常之后就不能继续?
为什么C++中没有相当于realloc()的函数?
如何使用异常?
怎样从输入中读取一个字符串?
为什么C++不提供“finally”的构造?
什么是自动指针(auto_ptr),为什么没有自动数组(auto_array)?
可以混合使用C风格与C++风格的内存分派与重新分配吗?
我为什么必须使用一个造型来转换*void?
我如何定义一个类内部(in-class)的常量?
为什么delete不会将操作数置0?
我能够写“voidmain()”吗?
为什么我不能重载点符号,:
:
,sizeof,等等?
怎样将一个整型值转换为一个字符串?
“int*p”正确还是“int*p”正确?
对于我的代码,哪一种布局风格(layoutstyle)是最好的?
我应该将“const”放在类型之前还是之后?
使用宏有什么问题?
特别是在一个学期的开始,我常常收到许多关于编写一个非常简单的程序的询问。
这个问题有一个很具代表性的解决方法,那就是(在你的程序中)读入几个数字,对它们做一些处理,再把结果输出。
下面是一个这样做的例子:
#include
usingnamespacestd;
intmain()
{
vectorv;
doubled;
while(cin>
>
d)v.push_back(d);
//读入元素
if(!
cin.eof()){//检查输入是否出错
cerr<
<
"
formaterror\n"
;
return1;
//返回一个错误
}
cout<
read"
<
v.size()<
elements\n"
reverse(v.begin(),v.end());
elementsinreverseorder:
\n"
for(inti=0;
i
return0;
//成功返回
对这段程序的观察:
这是一段标准的ISOC++程序,使用了标准库(standardlibrary)。
标准库工具在命名空间std中声明,封装在没有.h后缀的头文件中。
如果你要在Windows下编译它,你需要将它编译成一个“控制台程序”(consoleapplication)。
记得将源文件加上.cpp后缀,否则编译器可能会以为它是一段C代码而不是C++。
是的,main()函数返回一个int值。
读到一个标准的向量(vector)中,可以避免在随意确定大小的缓冲中溢出的错误。
读到一个数组(array)中,而不产生“简单错误”(sillyerror),这已经超出了一个新手的能力——如果你做到了,那你已经不是一个新手了。
如果你对此表示怀疑,我建议你阅读我的文章“将标准C++作为一种新的语言来学习”("
LearningStandardC++asaNewLanguage"
),你可以在本人著作列表(mypublicationslist)中下载到它。
!
cin.eof()是对流的格式的检查。
事实上,它检查循环是否终结于发现一个end-of-file(如果不是这样,那么意味着输入没有按照给定的格式)。
更多的说明,请参见你的C++教科书中的“流状态”(streamstate)部分。
vector知道它自己的大小,因此我不需要计算元素的数量。
这段程序没有包含显式的内存管理。
Vector维护一个内存中的栈,以存放它的元素。
当一个vector需要更多的内存时,它会分配一些;
当它不再生存时,它会释放内存。
于是,使用者不需要再关心vector中元素的内存分配和释放问题。
程序在遇到输入一个“end-of-file”时结束。
如果你在UNIX平台下运行它,“end-of-file”等于键盘上的Ctrl+D。
如果你在Windows平台下,那么由于一个BUG它无法辨别“end-of-file”字符,你可能倾向于使用下面这个稍稍复杂些的版本,它使用一个词“end”来表示输入已经结束。
//读入一个元素
cin.eof()){//检查输入是否失败
cin.clear();
//清除错误状态
strings;
cin>
s;
//查找结束字符
if(s!
="
end"
){
//返回错误
更多的关于使用标准库将事情简化的例子,请参见《C++程序设计语言》中的“漫游标准库”("
TouroftheStandardLibrary"
)一章。
你的编译器可能有问题。
也许它太老了,也许你安装它的时候出了错,也许你用的计算机已经是个古董。
在诸如此类的问题上,我无法帮助你。
但是,这也是很可能的:
你要编译的程序设计得非常糟糕,以至于编译器不得不检查数以百计的头文件和数万行代码。
理论上来说,这是可以避免的。
如果这是你购买的库的设计问题,你对它无计可施(除了换一个更好的库),但你可以将你自己的代码组织得更好一些,以求得将修改代码后的重新编译工作降到最少。
这样的设计会更好,更有可维护性,因为它们展示了更好的概念上的分离。
看看这个典型的面向对象的程序例子:
classShape{
public:
//使用Shapes的用户的接口
virtualvoiddraw()const;
virtualvoidrotate(intdegrees);
//...
protected:
//commondata(forimplementersofShapes)
Pointcenter;
Colorcol;
};
classCircle:
publicShape{
voiddraw()const;
voidrotate(int){}
intradius;
classTriangle:
voidrotate(int);
Pointa,b,c;
这不是一件容易的事情:
确定哪些实现部分是对所有的派生类都有用的,并将之共享出来。
因此,与public接口相比,protected成员往往要做多得多的改动。
举例来说,虽然理论上“中心”(center)对所有的图形都是一个有效的概念,但当你要维护一个三角形的“中心”的时候,是一件非常麻烦的事情——对于三角形,当且仅当它确实被需要的时候,计算这个中心才是有意义的。
protected成员很可能要依赖于实现部分的细节,而Shape的用户(译注:
user此处译为用户,指使用Shape类的代码,下同)却不见得必须依赖它们。
举例来说,很多(大多数?
)使用Shape的代码在逻辑上是与“颜色”无关的,但是由于Shape中“颜色”这个定义的存在,却可能需要一堆复杂的头文件,来结合操作系统的颜色概念。
当protected部分发生了改变时,使用Shape的代码必须重新编译——即使只有派生类的实现部分才能够访问protected成员。
于是,基类中的“实现相关的信息”(informationhelpfultoimplementers)对用户来说变成了象接口一样敏感的东西,它的存在导致了实现部分的不稳定,用户代码的无谓的重编译(当实现部分发生改变时),以及将头文件无节制地包含进用户代码中(因为“实现相关的信息”需要它们)。
有时这被称为“脆弱的基类问题”(brittlebaseclassproblem)。
一个很明显的解决方案就是,忽略基类中那些象接口一样被使用的“实现相关的信息”。
换句话说,使用接口,纯粹的接口。
也就是说,用抽象基类的方式来表示接口:
//使用Shapes的用户的接口
virtualvoiddraw()const=0;
virtualvoidrotate(intdegrees)=0;
virtualPointcenter()const=0;
//没有数据
Pointcenter()const{returncenter;
}
Pointcent;
Pointcenter()const;
现在,用户代码与派生类的实现部分的变化之间的关系被隔离了。
我曾经见过这种技术使得编译的时间减少了几个数量级。
但是,如果确实存在着对所有派生类(或仅仅对某些派生类)都有用的公共信息时怎么办呢?
可以简单把这些信息封装成类,然后从它派生出实现部分的类:
//nodata
structCommon{
publicShape,protectedCommon{
要清楚,两个不同的对象的地址也是不同的。
基于同样的理由,new总是返回指向不同对象的指针。
看看:
classEmpty{};
voidf()
Emptya,b;
if(&
a==&
b)cout<
impossible:
reporterrortocompilersupplier"
Empty*p1=newEmpty;
Empty*p2=newEmpty;
if(p1==p2)cout<
有一条有趣的规则:
一个空的基类并不一定有分隔字节。
structX:
Empty{
inta;
voidf(X*p)
void*p1=p;
void*p2=&
p->
a;
nice:
goodoptimizer"
这种优化是允许的,可以被广泛使用。
它允许程序员使用空类以表现一些简单的概念。
现在有些编译器提供这种“空基类优化”(emptybaseclassoptimization)。
不必须。
如果一个接口不需要数据时,无须在作为接口定义的类中赋予数据。
代之以在派生类中给出它们。
参见“为什么编译要花这么长的时间?
”。
有时候,你必须在一个类中赋予数据。
考虑一下复数类的情况:
templateclasscomplex{
complex():
re(0),im(0){}
complex(Scalarr):
re(r),im(0){}
complex(Scalarr,Scalari):
re(r),im(i){}
complex&
operator+=(constcomplex&
a)
{re+=a.re;
im+=a.im;
return*this;
private:
Scalarre,im;
设计这种类型的目的是将它当做一个内建(built-in)类型一样被使用。
在声明处赋值是必须的,以保证如下可能:
建立真正的本地对象(genuinelylocalobjects)(比如那些在栈中而不是在堆中分配的对象),或者使某些简单操作被适当地inline化。
对于那些支持内建的复合类型的语言来说,要获得它们提供的效率,真正的本地对象和inline化都是必要的。
因为很多类并不是被设计作为基类的。
例如复数类。
而且,一个包含虚拟函数的类的对象,要占用更多的空间以实现虚拟函数调用机制——往往是每个对象占用一个字(word)。
这个额外的字是非常可观的,而且在涉及和其它语言的数据的兼容性时,可能导致麻烦(例如C或Fortran语言)。
要了解更多的设计原理,请参见《C++语言的设计和演变》(TheDesignandEvolutionofC++)。
只有类在行为上是它的派生类的接口时(这些派生类往往在堆中分配,通过指针或引用来访问),虚拟函数才有意义。
那么什么时候才应该将析构函数定义为虚拟呢?
当类至少拥有一个虚拟函数时。
拥有虚拟函数意味着一个类是派生类的接口,在这种情况下,一个派生类的对象可能通过一个基类指针来销毁。
例如:
classBase{
virtual~Base();
classDerived:
publicBase{
~Derived();
Base*p=newDerived;
deletep;
//虚拟析构函数保证~Derived函数被调用
如果基类的析构函数不是虚拟的,那么派生类的析构函数将不会被调用——这可能产生糟糕的结果,例如派生类的资源不会被释放。
虚拟调用是一种能够在给定信息不完全(givenpartialinformation)的情况下工作的机制。
特别地,虚拟允许我们调用某个函数,对于这个函数,仅仅知道它的接口,而不知道具体的对象类型。
但是要建立一个对象,你必须拥有完全的信息。
特别地,你需要知道要建立的对象的具体类型。
因此,对构造函数的调用不可能是虚拟的。
当要求建立一个对象时,一种间接的技术常常被当作“虚拟构造函数”来使用。
有关例子,请参见《C++程序设计语言》第三版15.6.2.节。
设计思想是,用户通过Shape的public接口来操纵它们,而派生类(例如Circle和Triangle)的实现部分则共享由protected成员表现的那部分实现(implementation)。
下面这个例子展示一种机制:
如何使用一个抽象类来建立一个适当类型的对象。
structF{//对象建立函数的接口
virtualA*make_an_A()const=0;
virtualB*make_a_B()const=0;
voiduser(constF&
fac)
A*p=fac.make_an_A();
//将A作为合适的类型
B*q=fac.make_a_B();
//将B作为合适的类型
str
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 风格 技巧