转向C++0x和OOP之前,需要掌握其他一些技巧。
C++命令行参数:
main函数须这样定义:
int main(int argc, char* argv[]){/*...*/} //argc计数包括程序名,所以至少是1;argv[0]就是程序名。这两个参数可以任意使用,但是是只读的,可以随意显示或复制它们。程序中可以通过argc判断输入参数是否足够(例如大于1),通过argv判断各参数是否合法,合法的话存入其他变量使用;不合法的话提示输入后,存入其他变量中使用即可。MAX_PATH:系统能支持的文件路径名的最大长度。
除了#define, const也可用来声明常量。
C++支持重载(overloading,也称作复用):重复使用同一个函数名去处理不同类型的数据,使用时根据入参类型区分。重载函数的每一个版本都须有其函数声明和定义。 //it:类型名称不同但实际类型相同(例如typedef之后结构体的两种用法)是否算重载? C及绝大多数非OOP语言不支持重载,只能通过函数名区分。为何?(!!!OOP樶核心概念之一:数据类型决定着函数或操作符的行为。相关话题是操作符重载:有些操作符(例如+ -)可以根据操作数的类型进行不同的处理,可以为自己的类型编写操作符函数。如果操作数的类型发生变化,编译器将生成不同的指令。it:其实加法作用于整数之间跟作用于整数和指针之间,明显机制不同。)重载还算不上这个核心概念的完美实现,在某些场合,类型信息可能到了编译阶段也还是不够完备,这时就需要多态编程技术大显身手了。(后续讲)
do{...}while(x)
switch(x)
{
case 1:
...
break; //控制权将转移至switch语句末尾,否则将转入下一个case语句
case 2:
...
...
default:
...
}
多模块:可以使用1个以上的源代码模块(.cpp)。
同一个函数只能放进一个文件,不允许分割放入多个文件。
####重要规则####
1、每个函数的定义只能出现在某一个源文件里(it:编译器编译各个模块时检查是否有重复声明(?)和定义,其他文件的static函数定义不会被本文件编译检查到。) //it: 头文件命名和放置位置有何规则?
2、每个源文件都必须有某个函数的声明(原型)才能调用该函数。C++编译器会在源文件里为每一个函数原型寻找与之对应的函数定义,如果找不到,编译器会假定该函数是在另一个模块里定义的。
如果某个函数需要被多个模块共享,最好办法是把它和其他共享函数的函数声明都集中放置到一个文件里去,即所谓头文件,然后在每个模块开头加上一条#include语句。那些声明就会自动成为每个模块的一部分。
用类似方法共享数据变量:
C/C++中,函数外定义的变量都是全局变量,可以在随后定义的任何函数里使用。变量默认是创建它的模块的“私有财产”。若想共享它们,就需要在每个文件(或每个头文件)里加上extern声明(extern关键字是告诉编译器该变量是在其他文件中声明的)(it:意思是从外部导入的变量)。extern用来说明变量在程序其他地方声明,有可能(但不必要)是在另一个模块里,但这种变量必须保证只在某一个模块里被创建了一次。
多模块对大型软件项目非常有用,有利于码农分工协作(实现自己模块功能和接口),以及对程序进行按逻辑划分,以及控制各个模块之间相互通信的程度。
在C这样的老语言里,使用彼此相对独立的模块是隔离私有符号和公用(共享)符号的唯一手段。(模块中的私有部分对外部不可见,不用担心其他模块来调用私有函数或访问私有数据)。
这种“公私分明”做法是产生封装效果的方法之一。(封装是C++类带来的好处之一。it:公私分明可以用类来实现,而非像C一样必须用多模块来实现。)
写好各个模块源码之后,就可将.cpp分别编译成.o,随后链接C++库,即可生成.exe。(IDE设置好之后可自动完成此过程)
(it:sourceInsight是否可直接编译?it:应可,但c代码是为linux编写的,若不支持跨平台,那编译基本通不过(例如文件路径格式就不对。怎样跨平台?))
异常处理:
大型程序才最有用。
其实除了语法错误和程序逻辑错误以外,还可以划分出第三类错误:异常。涵盖各种运行错误(例如0为除数、错用空指针、内存已满申请不到、算术计算溢出)。
异常处理的关键是对非正常情况作出平稳合理的处理,包括输出日志信息用于发现和定位。在大型程序里返回错误码要层层传递向上返回才能到达与用户互动的层次,这是大型程序里让人头痛的地方。(it:做好错误码的分类分层次就很重要)
try-catch异常处理:
除最古老版本外,C++如今版本都支持使用try-catch-throw关键字来处理各种异常。异常处理把程序中的出错处理集中到了一处,无论哪里发生错误,控制权都能跳到正确的位置。
try
{
...
throw exception_object; //例如throw 12;会抛出一个int类型的异常。异常可以是任何一种类型
}
catch(exception_type [arguments]) //异常类型与exception_type相匹配才会进入,[arguments]是可选的
//it:多个catch后带同一exception_type会怎样?不匹配会继续检查下一个catch块,若该异常一直不能被捕获,程序将终止运行
{
...
}
许多类型异常是系统抛出的,它们全都派生自exception类,因而以下代码可捕获所有系统抛出的异常:
...
catch (exception &e)
{
cout<< "raised:"<<e.what()<<endl;
}
注意:
若想捕获“文件错误”类别的异常(例如 “file not found”),必须在打开文件之前先调用相关ifstream对象的exception函数:
ifstream in_file;
in_file.exceptions(ifstream::eofbit | ifstream::failbit | ifstream::badbit);
小结捡漏:
要想访问命令行参数,就要在声明main函数时给它增加argc和argv两个各个入参(it:只增加一个可否?)
argv是一个字符串指针数组。
重载函数在编译器件被区分了。(除了函数名字相同,这些函数其实毫不相关,干的活可以大相径庭)
只要有一个以上的函数或全局数据声明,都可把程序划分为多个源文件,其每一个都是一个模块。
一个函数可以调用在另一个模块中定义的函数,前提是前者所在模块必须引入后者的原型。把所有公用函数原型集中起来放到每个模块开始部分早已是码农共识。而把原型集中放到头文件里,是方便措施。(it:如何避免重复声明?it:之所以代码库搜到那么多重名函数却互不干扰,是因为实际编译时只能看到一个?)
#include "kissyou.h"
将在整个程序范围内共享的变量,需要在每一个相关模块里对之做出extern声明,同时,需保证在且仅在某一个模块里使用标准变量声明对此类变量进行定义。(it:若是部分文件共享,在其中一个文件中定义,在其他文件中用extern导入(或者在这些文件公用的头文件中extern导入)。若是大部分模块都要用,或者保证不会重名,或者暂时不用的模块将来可能要用,那就在被工程各个模块都include的头文件中放入extern声明。)
it:使用其他模块的函数要引入其原型;使用其他模块定义的变量要在本模块用extern声明。