(整理说明:本资料是我在网上无意间找到的,读起来感觉不错,但由于原文是每章一个网页的格式,读起来不是很习惯,而且也不方便保存,所以我花了2个多小时的时间将所有网页的内容综合整理了一下,但最后才发现,文章的顺序颠倒了,所以各位如果愿意阅读本文的话,请从后面向前读,每个红色“标题”代表一章,如有不便还请各位见谅,或到原文网站阅览)
标题:重载函数再论
重载函数是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来重载,比如引用传递和指针传递。
标题::文件的组织
一个程序往往由多个源文件组成,这些代码究竟应该放在哪个源文件里、哪些代码可以放在同一个源文件里、哪些代码必需分开放。这是一个管理层面的问题。
说它是管理层面的问题,是因为这些代码的组织往往没有惟一的准则。但是它们还是有一定的规律的。
首先,软件的维护是一个复杂的系统工程。代码的组织应该有利于维护。应该尽量把直接相关的内容放在同一文件、不相关的内容放在不同的文件里。如果这些代码还有亲和疏,那就要分不同的文件夹来存放了。
其次,软件的代码是一个严格的组织体系。不同的内容之间可能是并列的,也可能有必要的先后关系。于是在“#include”的时候要注意顺序。
最后,也是最重要的一点,有些代码在同一工程中可以重用(或必须重用),有些代码在同一个工程中只能出现一次。可以重用的有类的声明、函数的声明、变量的声明等,不可以重用的是类的实体、函数的实体、变量的定义等。那么,把可以重用的内容放在h文件中,把不可以重用的放在cpp文件中是一个好办法。
拿类的声明和类的实体为例,如果把一个类的所有内容一古脑放在同一个文件中,将可能出现问题。因为在其它用到类实例的地方都必须让类的声明“可见”,所以我们往往在文件头部加个“#include”,结果类的实体也被编译多次,在连接时产生冲突。
在前文中曾提到过,内联函数是惟一允许(也是必须)在编译时让函数实体可见的的函数。所以内联函数可以放在h文件中。C++规则中有一句正好与此照应:在类的声明中直接写出的函数被认为是内联函数。
Visual C++给类的文件起默认名时,文件名往往与类名一致。如果类名由“C”开头,则文件会是除去开头的“C”字以外的其它文字。如类“CMyClass”,它的代码存放在以下两个文件中:“MyClass.h”和“MyClass.cpp”中。原因是VC++建议类名以C开头,至于为什么在文件名中不出现开头的“C”,可能是出于微软的习惯吧。
标题::类的构造函数
引用:构造函数是特殊的成员函数。
笔记:构造函数的确是一类“特殊”的成员函数。它的特殊性至少表现在以下几个方面:一是它的调用不用程序员操心,只要类对象被创建它就会被调用,而且它不允许被程序员显式地调用。二是它们是必需的,如果程序员偷懒,编译器将自动创建简单的构造函数。三是它们的名字不用程序员多考虑,直接与类名相同。四是它们没有返回值。
下面详说这几个特性:
一、它们在类对象被创建时自动调用,创建对象可能有以下方法:程序中用声明变量的语句直接声明创建,或者在程序中用new关键字动态创建。这两种方法都可以创建单个对象,也都可以创建对象数组。只要有一个对象被创建,构造函数就被调用一次。
如果程序员想显式地调用构造函数那是不行的。正因为如此,构造函数中还有一种特定的部分叫“初始化列表”,通过它程序员可以调用基类或成员的构造函数。必竟类的设计千差万别,如果某个类的基类或(和)成员有多个构造函数,那么,该类必须能够指定用哪一个构造函数,否则类的功能将大打折扣。调用构造函数不是程序员的事,程序员不应该管也管不了。初始化列表为解决这个问题而生,所以只有构造函数才有初始化列表,其它函数不能有。
上面说到的“大打折扣”究竟是怎样的折扣呢?如果不能指定基类和成员用哪一个构造函数,那就只好让编译器去挑了,构造出来的对象往往不符合要求,只好调用基类和成员的其它函数,比如赋值函数或其它进行参数设定的函数——当然,基类和成员必须包含这样的函数。这样就浪费了资源。
二、类必须包含构造函数——确切地说是必须包含无参数构造函数和拷贝构造函数——原因是因为它们的调用是自动的。如果这两个函数根本就没有,你让系统如何调用?所以,C++也不含糊,你要是懒得写,它就帮你写一个简单的。简单就意味着至少要丧失一些功能,如果类设计得比较复杂(比如包含指针操作)还可能引起灾难性事故。
三、函数名与类名一致。构造函数的名称是必须特殊的,即使这个特殊不表现在与类名相同,也必须找到另一个规则来实现。因为系统要自动调用这些函数,你就必须让系统知道哪些函数是构造函数。
第四个特性直接改变了C/C++语言的一条规则:C语言规定,如果函数没有明显指出返回类型,那么C语言认为返回值是int型。C语言之所以可以有这条规则,一是因为返回int的函数很多,二是因为即使没有返回值,也必须指明void。当时制定规则的人无法预料到,C++中居然会出现“连void都不是的返回值”的函数,void虽然表示不返回任何值,必竟与类构造函数的“没有返回值”是两码事。于是,C++新标准规定:在定义或声明函数时,没有显式指定返回类型中不合法的。当然类的构造函数除外。
构造函数的出现有它的可行院捅厝恍浴?尚行允怯捎贑++的类允许包含成员函数,既然类可以包含普通的成员函数,那么包含特殊的函数自然也不在话下。必然性是由于类的对象往往必须经过特定的初始化。C++到来之前,C语言中的数据类型只是内置类型。对于内置类型对象,如果忘了初始化,大不了这个对象失去作用,但是不会导致大的问题。比如一个int型值,无论内存如何随机,它的取值范围都不会超过int能表达的范围,对它进行运算也不会产生危险(溢出不能算危险,即使初始化过的数据也不能保证不溢出,而且溢出只是一种逻辑问题)。但是现在的类不这么简单了,忘了初始化往往将带来运行错误。于其每次都要考虑数据的初始化,还不如把这个初始化写成统一的函数,让系统自动调用来得既安全又方便。
标题::类的成员函数
类与C语言中的结构体最大的区别就是类可以带函数,而结构体只是一个内存组合。所以,要提类就不得不提成员函数。
类的成员函数与普通函数(全局函数)相比,最根本的区别是实现了类的封装性。封装性的第一个表现是访问权限:都是函数,但是你能访问哪个不能访问哪个却可以设定。第二个表现是直观,通过类成员(或指针)来调用函数,给人的直觉就是“这是类提供的功能”。你好像“Bird.Fly();”一样一目了然。
在理解this指针以前要想彻底理解成员函数是有困难的,我就曾以为在类的实例中保存了函数的副本。要不然,为什么同一个类的不同对象调用这个函数有不同的效果呢?原来,在函数所有的形参之外,还有一个不用你操心的参数this,它是一个指针,该指针的目标就是函数的调用者。这么一说就明白了。
函数形参表后加入const就成了“const成员函数”,这样的函数保护了调用者自身不被修改。如CString的GetLength()函数,你只能获取它的长度,不能修改它的内容或长度。加入const的作用倒不是怕调用者修改,而是防止编写函数的人不小心改动了对象。因为百密总有一疏,万一在某个不该修改数据的函数中改变了数据(比如将“==”写成“=”),或者万一调用了另一个非const的成员函数都将可能引起错误。在编写函数前就先加上const可以记编译器来帮你检查。
这个const加在形参表的后面显得有些怪怪的,造成“怪怪的”原因就是因为函数的形参表中没有this,也就没有能用const来修饰的东西了。林锐说“大概是因为其它地方都已经被占用了”并不是根本原因。
标题::内联函数
内联函数应该是为了改善C语言中的宏替换的不足而产生的吧。因为宏替换是预编译中直接展开的,展开过程中将产生意想不到的结果。典型的有“#define MAX(a, b) (a) > (b) ? (a) : (b)”。“result = MAX(i, j)+2;”将被展开为“result = (i) > (j) ? (i) : (j) + 2;”。虽然外面再加一对括号可以解决以上问题,但是“result = MAX(i++, j);”被展开后将导
分享到:
相关推荐
### C++ Primer 读书笔记概览 #### 操作系统与主函数 - **主函数返回值的意义**:操作系统依据`main`函数返回的值来判定程序执行的状态,其中,0值意味着程序成功运行至结束。非零返回值,具体含义由操作系统定义...
C++Primer中文第三版(C++从入门到精通)第一章的读书笔记,主要是C++程序、预处理器指示符、iostream库等的基础知识点读书笔记。
### C++ Primer 读书笔记概览 在深入探讨C++ Primer各章节的精要之前,我们先简要回顾一下这本经典教材的核心价值。《C++ Primer》是学习C++编程语言的必读之选,它不仅覆盖了C++的基础语法,还深入讲解了面向对象...
"C++ Primer读书笔记" 本文总结了C++ Primer读书笔记的要点,涵盖了C++语言的基础知识,包括变量、基本类型、常量、输入输出等。 1. 变量和基本类型 C++语言中有多种基本类型,包括整型、浮点型、字符型等。在...
C++ Primer 是一本经典的C++学习书籍,涵盖了C++的基础知识和高级特性。这篇学习笔记主要涉及了C++编程的一些核心概念,包括程序结构、变量、基本类型、初始化与赋值、可读性、常量与引用、typedef、枚举以及标准库...
C++primerplus笔记.pdf
### C++ Primer 读书笔记概览 #### 一、程序入口与返回值 - **main()函数**: C++程序的入口点通常是`main()`函数。根据C++标准,`main()`函数可以没有显式的`return`语句。在这种情况下,程序会自动返回0,表示...
C++是一门强大的编程语言,其包含了...以上就是C++ Primer学习笔记中涵盖的一些主要知识点。通过这些知识点的学习,可以帮助我们深入理解C++语言的核心概念和编程范式,从而编写出更加高效、安全和可维护的C++代码。
C++ primer plus 第五版的个人学习笔记,仅供大家学习参考。
《C++ Primer学习笔记》是一份详尽的资料,涵盖了C++编程语言的基础到高级概念。这份笔记旨在帮助初学者和有一定经验的开发者深入理解C++的语法、特性以及最佳实践。C++ Primer是C++编程领域的一本经典教材,以其...
《C++ Primer 全书笔记》是一份专为C++初学者设计的学习资源,由一位计算机专业学生在找工作的过程中为了复习C++ Primer而编写的详细笔记。笔记以易懂的方式介绍了C++的基础概念和核心特性,对于那些基础知识不扎实...
根据给定的信息,“c++ primer笔记”主要围绕经典著作《C++ Primer》展开,这是一本深受程序员喜爱的书籍,旨在帮助读者系统地学习和掌握C++编程语言的基础及高级特性。下面将从几个关键方面对C++的核心知识点进行...
内容概要:这是关于《C++ Primer学习笔记》的文章,涵盖多种类型转换、异常机制、类静态成员、IO库介绍、顺序容器操作、泛型算法应用、关联容器概念与使用详解、动态内存智能指针管理、构造函数与拷贝构造细节探讨、...
《C++ Primer 学习笔记》是一份针对C++初学者的重要参考资料,它基于C++ Primer第三版的内容,旨在帮助读者深入理解C++这门强大的编程语言。C++ Primer是学习C++的经典书籍,以其全面、深入且易于理解的讲解闻名,这...
C++ Primer 笔记 本笔记基于 C++ Primer 的学习笔记,涵盖了友元函数、操作符重载、运算符重载、using 声明和引用等多个知识点。 友元函数 友元函数是一种特殊的函数,它可以访问类的私有成员。通过让函数成为类...
首先,C++ Primer 笔记会涵盖C++的基础语法,包括变量、数据类型(如int、char、float等)、运算符(算术、比较、逻辑、位操作等)以及流程控制(如if语句、switch语句、for循环、while循环)。这些构成了任何编程...
C++ primer plus学习笔记之三,分为一下几个部分: 函数参数:介绍了函数的生命规则以及定义 数组函数:数组作为变量时的使用方法 指针和const:灵活运用指针和const 函数和二维数组:二维数组作为变量时声明以及定义...