文档来源为:从网络收集整理.word版本可编辑.欢迎下载支持.
(从后向前看) 标题:重载函数再论
重载函数是C++提出来的概念,但是在C中却未必没有。比如“1+3”和“1.0+3.0”,虽然都是加法,做的却不是同的操作:编译器要因操作数的不同而调用不同的加法操作。只是C语言中除了内部类型变量可以参与运算以外,没有“类”这么高深的概念。“结构体”也只是内存数据的组织方法,而不涉及对整个结构体的处理。所以,在C语言时代编译器明明做了类似于重载的事情,却可以像雷锋一样“做好事不留名”。 C++发展出了类,并且赋予了“类”很高的期望,类的对象也能像内置类型对象一样参与一切运算。那么,就拿加法运算来说,编译器如何知道对某类对象的加法该调用哪一个详细的操作代码?于是,即使不出现普通函数的重载,至少运算符是要重载的。
林锐博士在《高质量C++/C编程指南》中为重载函数的必要性提了另一个理由:类的构造函数名称必须与类名相同,而类却经常要定义多个不同的构造函数。那就只好重载了。
对于普通程序员来说,我们完全可以不用考虑得这么深。重载函数给我们至少还带来了另一个好处:不用记忆多个不同的函数名了,也不用为了给函数起名而绞尽脑汁了。不过本书还给出了一个建议:并不是任何时候都有必要重载函数的,有的时候不同的函数名可以直观地带来好多信息,滥用重载只是牺牲了名称中的信息。 标题::重载函数的概念
引用:出现在相同作用域中的两个(可以是两个以上——偷猫注)函数,如果具有相同的名字而形参表不同,则称为重载函数。
本节开头第一句话就给出了重载函数的定义:重载函数必须符合两个条件:一是出现在相同的作用域中、二是函数名字相同而形参表不同。
其中第一个条件一般人往往是不去想的,其实函数名相同而作用域不同的函数大大存在,比如在MFC中就有。它们是完全不相干的函数。
第二个条件还可以详说一下:函数名字相同当然不在话下,这是函数被称为“重载”的根源。之于形参表不同,可能表现在形参个数不同、可能表现在形参类型不同、还可能表现在形参顺序不同。 如果要扩展开来说,还可以举出许多不是重载函数的情况。
一、如果既在同一作用域下、名称也相同、形参表也相同,则后者被视为前者的重复声明。——函数可以重复声明,因为函数的声明并不产生目标代码,但是函数的定义不允许重复出现。
二、如果既在同一作用域下、名称也相同、形参表也相同,但是返回值不同,则后者被视为错误的声明。函数不可以只凭返回值来区分,因为调用函数的时候只凭名称和形参来选择函数,而不凭返回值。再究其原因,一是因为函数的返回值可以被丢弃;二来即使不丢弃,将返回值赋予另一个变量之前没必要检查我需要什么样的返回值,而能否赋值也与函数本身无关。
三、有些时候看起来形参表不同,实际上是完全相同的,书本第229页讲了四组这样的例子: Record lookup(const Account &acct);
Record lookup(const Account &);//区别在于有没有给形参命名 typedef Phone Telno;
Record lookup(const Phone&);
Record lookup(const Telno&);//只是给类型取了个别名 Record lookup(const Phone&, const Name&);
Record lookup(const Phone&, const Name& = \区别在于给形参提供了默认值 Record lookup(Phone);
Record lookup(const Phone);//区别在于是否const
其中第三组可能会让人产生函数的形参个数不同的假像,其实可缺省的形参并没有减少形参的个数。第四组有点不容易搞清:因为有的时候可以凭是否const来重载,比如引用传递和指针传递。 标题::文件的组织
文档来源为:从网络收集整理.word版本可编辑.欢迎下载支持.
一个程序往往由多个源文件组成,这些代码究竟应该放在哪个源文件里、哪些代码可以放在同一个源文件里、哪些代码必需分开放。这是一个管理层面的问题。
说它是管理层面的问题,是因为这些代码的组织往往没有惟一的准则。但是它们还是有一定的规律的。 首先,软件的维护是一个复杂的系统工程。代码的组织应该有利于维护。应该尽量把直接相关的内容放在同一文件、不相关的内容放在不同的文件里。如果这些代码还有亲和疏,那就要分不同的文件夹来存放了。
其次,软件的代码是一个严格的组织体系。不同的内容之间可能是并列的,也可能有必要的先后关系。于是在“#include”的时候要注意顺序。
最后,也是最重要的一点,有些代码在同一工程中可以重用(或必须重用),有些代码在同一个工程中只能出现一次。可以重用的有类的声明、函数的声明、变量的声明等,不可以重用的是类的实体、函数的实体、变量的定义等。那么,把可以重用的内容放在h文件中,把不可以重用的放在cpp文件中是一个好办法。
拿类的声明和类的实体为例,如果把一个类的所有内容一古脑放在同一个文件中,将可能出现问题。因为在其它用到类实例的地方都必须让类的声明“可见”,所以我们往往在文件头部加个“#include”,结果类的实体也被编译多次,在连接时产生冲突。
在前文中曾提到过,内联函数是惟一允许(也是必须)在编译时让函数实体可见的的函数。所以内联函数可以放在h文件中。C++规则中有一句正好与此照应:在类的声明中直接写出的函数被认为是内联函数。 Visual C++给类的文件起默认名时,文件名往往与类名一致。如果类名由“C”开头,则文件会是除去开头的“C”字以外的其它文字。如类“CMyClass”,它的代码存放在以下两个文件中:“MyClass.h”和“MyClass.cpp”中。原因是VC++建议类名以C开头,至于为什么在文件名中不出现开头的“C”,可能是出于微软的习惯吧。 标题::类的构造函数
引用:构造函数是特殊的成员函数。
笔记:构造函数的确是一类“特殊”的成员函数。它的特殊性至少表现在以下几个方面:一是它的调用不用程序员操心,只要类对象被创建它就会被调用,而且它不允许被程序员显式地调用。二是它们是必需的,如果程序员偷懒,编译器将自动创建简单的构造函数。三是它们的名字不用程序员多考虑,直接与类名相同。四是它们没有返回值。 下面详说这几个特性:
一、它们在类对象被创建时自动调用,创建对象可能有以下方法:程序中用声明变量的语句直接声明创建,或者在程序中用new关键字动态创建。这两种方法都可以创建单个对象,也都可以创建对象数组。只要有一个对象被创建,构造函数就被调用一次。
如果程序员想显式地调用构造函数那是不行的。正因为如此,构造函数中还有一种特定的部分叫“初始化列表”,通过它程序员可以调用基类或成员的构造函数。必竟类的设计千差万别,如果某个类的基类或(和)成员有多个构造函数,那么,该类必须能够指定用哪一个构造函数,否则类的功能将大打折扣。调用构造函数不是程序员的事,程序员不应该管也管不了。初始化列表为解决这个问题而生,所以只有构造函数才有初始化列表,其它函数不能有。
上面说到的“大打折扣”究竟是怎样的折扣呢?如果不能指定基类和成员用哪一个构造函数,那就只好让编译器去挑了,构造出来的对象往往不符合要求,只好调用基类和成员的其它函数,比如赋值函数或其它进行参数设定的函数——当然,基类和成员必须包含这样的函数。这样就浪费了资源。
二、类必须包含构造函数——确切地说是必须包含无参数构造函数和拷贝构造函数——原因是因为它们的调用是自动的。如果这两个函数根本就没有,你让系统如何调用?所以,C++也不含糊,你要是懒得写,它就帮你写一个简单的。简单就意味着至少要丧失一些功能,如果类设计得比较复杂(比如包含指针操作)还可能引起灾难性事故。
三、函数名与类名一致。构造函数的名称是必须特殊的,即使这个特殊不表现在与类名相同,也必须