早期语言(如 Fortran 和 Basic )在代码组织上可能会存在问题(例如分支语句常常会让代码逻辑和可读性变得特别差)。对此,C语言使用了结构化编程这一方法,将程序中的分支限制为一组易读的代码结构(例如 for、while、do while 循环以及 if else 语句)。
将大型程序分解为多个小型、易管理、可重复调用的小任务,这个分解过程可以持续下去,把小任务分解为更小的任务。在C语言中,通过函数来实现“自顶向下”。
即 Procedure Oriented Programming(POP),以过程为中心的编程思想。以“什么正在发生”为主要目标进行编程,是 C语言的重要特性(前文所述的结构化编程、自顶向下思想亦根植于此特性)。由于 C++ 继承自 C,因此 C++ 也拥有此特性。
即 Object Oriented Programming(OOP)。在 C++ 中通过类与对象这两种数据结构及封装、多态、继承等方法实现。类规定了用何数据描述一个物像(或者概念)并规定了如何用这些数据去描述,对象则是根据类的要求构造的而成的,代表某个物像(或概念)的特殊数据结构。因此与面向过程不同,面向对象的重点不在任务上,而在于表示概念。
面向对象编程常常从低级组织(类)开始构建,进而构建高级组织(程序),该过程称为自下向上编程( bottom-up )
即Generic Programming。泛型也是一种特殊的抽象数据结构,相较于面向对象编程关注于数据,泛型编程关注算法本身,因此其不拘泥于数据类型。例如,写一个两数相加返回结果的函数,若输入的数据类型可能为int,也可能为float,则比较麻烦,通过泛型(更具体地说,使用模板)则可较为方便地解决:
#include
using namespace std;
template
T add(T a,T b){
return a+b;
}
int main(){
int n1, n2, n3;
float f1, f2, f3;
cin>>n1>>n2;
cin>>f1>>f2;
n3 = add(n1, n2);
f3 = add(f1, f2);
return 0;
}
可移植性是指同样的代码,只需要使用针对不同平台设计的编译器进行编译,而不必更改代码本身,就可以跨平台运行(这里所说的平台,是 CPU + 操作系统,也就是说,即使操作系统相同,CPU不同,也可能算作不同平台)。然而程序的可移植性的实现有两大阻碍。一是硬件,与硬件相关的程序不可移植。举个例子,汇编语言的几乎就是不可移植的,因为它过于底层,直接操作硬件,然而不同的CPU有不同的指令集(不同的指令集意味着不同的汇编语法),因此同样的汇编语言,可能换个CPU就不行了……二是标准。不同操作系统上,即使是同一种语言,由于标准不同语法上可能会有些许差异,这也是影响移植性的一大原因。
要解决第二种阻碍,可通过制定针对所有平台的统一标准来实现。对此,ISO标准委员会分别于1998、2003、2011年推出了C++标准(分别为C++98、C++03、C++11标准,其中C++03是C++98的补丁版,两者语言特性一致)。之后也ISO也颁布过C++14、C++17等标准……
同理,C语言也有国际标准,如C89、C90、C99等(其中C89和C90一样)
虽然C++一般可以视为功能更强大的C,但两者并不能完全兼容。C++标准可“近似地”视为C标准的超集,但也并不是所有的C语言的代码都可正确运行于C++中。
C++ Primer Plus 书中所述基于C++98标准,并介绍部分C++11特性。
下面我们将逐行解释以下程序
#include
using namespace std;
int main(){
int a, b, c;
cin>>a>>b;
c = a + b;
cout<
头文件的作用、编译器对头文件的处理方式(预编译)这里就不展开了,这里讲讲头文件的几种形式:
再讲讲语法规则:
如果include的是官方的标准头文件,用尖括号“包围”头文件名,如:
#include
预编译时编译器会在系统中存放标准头文件的目录中寻找。
如果是include的是用户编写的,非官方的头文件,则用双引号:
#include "abc.h"
这样编译器在预编译的时候会优先在用户目录下搜索对应头文件,没有的话再到系统中存放标准头文件的目录中寻找。
假设程序引用的两个头文件a和b中,都有函数add的声明,那在main中调用add就会产生冲突,因此可通过定义两个命名空间A和B进行对应,那么在函数调用时,A::add()调用的就是头文件a中声明的函数,B::add()调用的就是头文件b中声明的函数。
目前,C++标准的函数、类、变量等,都置于命名空间std之下,假设#include
using std::cout;
之后在使用cout时,可不必再写std::
当然,也可以直接声明使用std命名空间中的所有名称,从而在后续调用中免去所有std::
using namespace std;
该行命令可以写在函数内,则该函数从这条命令后的调用可省略std::,也可以写在函数外,则全局有效。
无论是单个cpp,还是大量文件的巨型工程,一般而言代码中有且只有一个main函数(没有main函数的其他cpp文件则被视为库文件,一般用于实现函数、类)。通常,我们视main函数为操作系统与程序的桥梁。系统通过main,来开始执行我们的源代码。
main函数无需参数传入,因此也可以写为:int main(void)
main函数一般写为int类型,返回值为0,然而实际上,根据C++标准,main函数可以省略掉return 0;当然也只有main函数有这个特殊待遇……
经典的C语言写法往往还能省略掉main前面的int:
main()
{
int a, b, c;
c = a + b;
cout<
此外还有写成void main()的,大一的垃圾C++课用的VC 6.0支持的就是这个……不过这种写法比较新的编译器都不支持了 。
没什么好说的,略了……
按书中所述,cin和cout并不是函数,而是一种对象,cin为istream类的对象,cout为ostream类的对象,这两个类均在iostream中定义。符号“<<”和“>>”是用于指示信息的流动方向,如cout<
也没啥好说的,记一个不太熟的知识点:C++支持连续赋值。
int a = 1;
int d = c = b = a;
赋值依次进行,a的值赋给b,此时b==1,再把b的1赋值给c,依次类推。
具体可看“第五行:cin”的相关内容,这里记个知识点:控制符“endl”实际上通过iostream声明,其全名为std::endl。
另外,endl是控制符,换行符是“\n”。
前文提到了,略了……
最后讲讲格式问题。
代码中不可分割的元素叫做标记(token),例如int、main、return这些关键字、变量名等,标记之间一般需要用空白来隔开。所谓空白,是指空格、制表符(Tab)或者回车。
空白用于隔开标记,但实际上,并未规定用何种标记来隔开标记,因此程序可以写成这个样子:
# include
using
namespace
std
;
int main
( )
{return 0;}
也真是有够恶心的……