指针作函数参数
指针作函数参数,形参要求是指针变量,实参要求是指针类型数据。 函数调用时,将实参的值传递给形参,形参与实参得到相同的值。这时形参所指向的数据与实参所指向的数据共享存储单元,在函数中改变了形参所指向的数据,对应实参所指向的数据将同时改变,函数调用后带回多个值,如此在C语言中间接实现数据的双向传递。 请注意,利用指针作函数参数实现数据的双向传递不是指指针参数本身,而是指隐藏在指针后面的指针指向变量,指针参数本身依然是单向传值。 例将两个整数按从小到大的顺序输出。 先定义一个函数,用指针变量作参数,实现两个数的...
EffectiveC++2eItem34
条款34: 将文件间的编译依赖性降至最低 假设某一天你打开自己的C++程序代码,然后对某个类的实现做了小小的改动。提醒你,改动的不是接口,而是类的实现,也就是说,只是细节部分。然后你准备重新生成程序,心想,编译和链接应该只会花几秒种。毕竟,只是改动了一个类嘛!于是你点击了一下"Rebuild",或输入make(或其它类似命令)。然而,等待你的是惊愕,接着是痛苦。因为你发现,整个世界都在被重新编译、重新链接! 当这一切发生时,你难道仅仅只是愤怒吗? 问题发生的原因在于,在将接口从实现分离这方面,C++做得...
EffectiveC++2eItem33
条款33: 明智地使用内联 内联函数------多妙的主意啊!它们看起来象函数,运作起来象函数,比宏(macro)要好得多(参见条款1),使用时还不需要承担函数调用的开销。你还能对它们要求更多吗? 然而,你从它们得到的确实比你想象的要多,因为避免函数调用的开销仅仅是问题的一个方面。为了处理那些没有函数调用的代码,编译器优化程序本身进行了专门的设计。所以当内联一个函数时,编译器可以对函数体执行特定环境下的优化工作。这样的优化对"正常"的函数调用是不可能的。 我们还是不要扯得太远。程序世界和现实生活一样,从...
EffectiveC++2eItem32
条款32: 尽可能地推迟变量的定义 是的,我们同意C语言中变量要放在模块头部定义的规定;但在C++中,还是取消这种做法吧,它没必要,不自然,而且昂贵。 还记得吗?如果定义了一个有构造函数和析构函数的类型的变量,当程序运行到变量定义之处时,必然面临构造的开销;当变量离开它的生命空间时,又要承担析构的开销。这意味着定义无用的变量必然伴随着不必要的开销,所以只要可能,就要避免这种情况发生。 正如我所知道的,你的编程方式优雅而不失老练。所以你可能会在想,你决不会定义一个无用的变量,所以本条款的建议不适用于你严谨...
EffectiveC++2eItem9
条款9. 避免隐藏标准形式的new 因为内部范围声明的名称会隐藏掉外部范围的相同的名称,所以对于分别在类的内部 和全局声明的两个相同名字的函数f来说,类的成员函数会隐藏掉全局函数: void f(); // 全局函数 class X { public: void f(); // 成员函数 }; X x; f(); // 调用 f x.f(); // 调用 X::f 这不会令人惊讶,也不会导致混淆,因为调用全局函数和成员函数时总是采用不同的 语法形式。然而如果你在类里增加了一个带多个参数的operator...
EffectiveC++2eItem10
条款10. 如果写了operator new就要同时写operator delete 让我们回过头去看看这样一个基本问题:为什么有必要写自己的operator new和operator delete? 答案通常是:为了效率。缺省的operator new和operator delete具有非常好的通用性,它的这种灵活性也使得在某些特定的场合下,可以进一步改善它的性能。尤其在那些需要动态分配大量的但很小的对象的应用程序里,情况更是如此。 例如有这样一个表示飞机的类:类Airplane只包含一个指针,它指向...
EffectiveC++2eItem38
条款38: 决不要重新定义继承而来的缺省参数值 让我们从一开始就把问题简化。缺省参数只能作为函数的一部分而存在;另外,只有两种函数可以继承:虚函数和非虚函数。因此,重定义缺省参数值的唯一方法是重定义一个继承而来的函数。然而,重定义继承而来的非虚函数是一种错误(参见条款37),所以,我们完全可以把讨论的范围缩小为 "继承一个有缺省参数值的虚函数" 的情况。 既然如此,本条款的理由就变得非常明显:虚函数是动态绑定而缺省参数值是静态绑定的。 什么意思?你可能会说你不懂这些最新的面向对象术语;或者,过度劳累的你...
EffectiveC++2eItem37
条款37: 决不要重新定义继承而来的非虚函数 有两种方法来看待这个问题:理论的方法和实践的方法。让我们先从实践的方法开始。毕竟,理论家一般都很耐心。 假设类D公有继承于类B,并且类B中定义了一个公有成员函数mf。mf的参数和返回类型不重要,所以假设都为void。换句话说,我这么写: class B { public: void mf(); ... }; class D: public B { ... }; 甚至对B,D或mf一无所知,也可以定义一个类型D的对象x, D x; // x是类型D的一个对象...
EffectiveC++2eItem36
条款36: 区分接口继承和实现继承 (公有)继承的概念看起来很简单,进一步分析,会发现它由两个可分的部分组成:函数接口的继承和函数实现的继承。这两种继承类型的区别和本书简介中所讨论的函数声明和函数定义间的区别是完全一致的。 作为类的设计者,有时希望派生类只继承成员函数的接口(声明);有时希望派生类同时继承函数的接口和实现,但允许派生类改写实现;有时则希望同时继承接口和实现,并且不允许派生类改写任何东西。 为了更好地体会这些选择间的区别,看下面这个类层次结构,它用来表示一个图形程序中的几何形状: clas...
EffectiveC++2eItem35
继承和面向对象设计 很多人认为,继承是面向对象程序设计的全部。这个观点是否正确还有待争论,但本书其它章节的条款数量足以证明,在进行高效的C++程序设计时,还有更多的工具听你调遣,而不仅仅是简单地让一个类从另一个类继承。 然而,设计和实现类的层次结构与C语言中的一切都有着根本的不同。只有在继承和面向对象设计领域,你才最有可能从根本上重新思考软件系统构造的方法。另外,C++提供了多种很令人困惑的面向对象构造部件,包括公有、保护和私有基类;虚拟和非虚拟基类;虚拟和非虚拟成员函数。这些部件不仅互相之间有联系,还...
EffectiveC++2eItem11
构造函数,析构函数和赋值操作符 几乎所有的类都有一个或多个构造函数,一个析构函数和一个赋值操作符。这没什么奇怪的,因为它们提供的都是一些最基本的功能。构造函数控制对象生成时的基本操作,并保证对象被初始化;析构函数摧毁一个对象并保证它被彻底清除;赋值操作符则给对象一个新的值。在这些函数上出错就会给整个类带来无尽的负面影响,所以一定要保证其正确性。本章我将指导如何用这些函数来搭建一个结构良好的类的主干。 条款11: 为需要动态分配内存的类声明一个拷贝构造函数和一个赋值操作符 看下面一个表示String对象的...
EffectiveC++2eItem12
条款12: 尽量使用初始化而不要在构造函数里赋值 看这样一个模板,它生成的类使得一个名字和一个T类型的对象的指针关联起来。 templateclass T class NamedPtr { public: NamedPtr(const string initName, T *initPtr); ... private: string name; T *ptr; }; (因为有指针成员的对象在进行拷贝和赋值操作时可能会引起指针混乱(见条款11),NamedPtr也必须实现这些函数(见条款2)) 在写Nam...