#include <iostream>
using namespace std;
int main()
{
std::cout << "Hello World!\n"; //输出Hello World!
return 0;
}
3.1 结构解析
3.2 main函数
3.2.1 #include
#include预处理指令的主要功能是在编译器进行源代码的编译过程之前,添加或者替换相应的预编译指令,从而使得用户源代码中调用的系统预定义函数和各种标识符能够正确地被编译器识别和编译。
#include <iostream>表示的含义是将iostream头文件添加到当前源代码中,iostream头文件包含了系统的标准输入/输出函数以及数据的声明和定义。
#include 命令告诉预处理器将指定头文件的内容插入到预处理器命令的相应位置。采用 #include 命令所插入的文件,通常文件扩展名是 .h,文件包括函数原型、宏定义和类型定义。只要使用 #include 命令,这些定义就可被任何源文件使用。
3.2.2 头文件
像iostream这样的文件叫做包含文件(include file),也叫头文件(header file);这是由于它们被包含在其他文件起始处。
头文件的定义:
头文件是以.h为后缀的文件;
一般而言,每个C++/C程序通常由头文件和定义文件组成。头文件作为一种包含功能函数、数据接口声明的载体文件,主要用于保存程序的声明,而定义文件用于保存程序的实现。
头文件的作用:
头文件的主要作用在于多个代码文件全局变量(函数)的重用、防止定义的冲突,对各个被调用函数给出一个描述,其本身不需要包含程序的逻辑实现代码,它只起描述性作用,用户程序只需要按照头文件中的接口声明来调用相关函数或变量,链接器会从库中寻找相应的实际定义代码。
3.2.3 函数头
C++句法要求main()函数的定义以函数头int main()开始。
C++函数可被其他函数激活或调用,函数头描述了函数与调用它的函数之间的接口。
3.2.3 函数体
函数体由一系列位于花括号({}) 中的C++语句组成。有多种类型的C++语句,包括下述6种。
1、声明语句:定义函数中使用的变量的名称和类型。
2、赋值语句:使用赋值运算符(=)给变量赋值。
3、消息语句:将消息发送给对象,激发某种行动。
4、函数调用:执行函数。被调用的函数执行完毕后,程序返回到函数调用语句后面的语句。
5、函数原型:声明函数的返回类型、函数接受的参数数量和类型。
6、返回语句:将一个值从被调用的函数那里返回到调用函数中。
拓展:
(1)表达式语句。
表达式语句导致计算表达式。 出于表达式语句的原因,不会发生控制或迭代的传输。
[expression ] ;
备注:
在执行下一个语句前,将计算表达式语句中的所有表达式并完成所有副作用。 最常用的表达式语句是赋值和函数调用。 由于表达式是可选的,因此分号单独被视为空表达式语句,称为 null 语句。
(2)Null 语句。
可以在 C++ 语法需要语句但未采取任何措施的位置提供这些语句。
"Null 语句" 是缺少 表达式 的表达式语句。 当语言的语法调用语句而不是表达式计算时,它很有用。 它包括分号。
Null 语句通常用作迭代语句中的占位符或用作在复合语句或函数的末尾放置标签的语句。
(3)复合语句。
复合语句由括在大括号中的零个或多个语句组成 ({ }) 。 可以在任何期望语句出现的位置使用复合语句。 复合语句通常称为“块”。
{ [ statement-list ] }
(4)选择语句。
这些语句执行一个测试;如果测试的计算结果为 true(非零),则它们将执行代码的一部分。 如果测试的计算结果为 false,则它们可能执行代码的另一部分。
C++ 选择语句( 如果 和 开关)提供了一种有条件地执行代码部分的方式。
使用 __if_exists 和 __if_not_exists 语句,你可以根据符号的存在有条件地包含代码。
(5)迭代语句。
这些语句用于重复执行代码块,直到满足指定的终止条件。
根据一些循环终止条件,迭代语句会导致语句(或复合语句)被执行零次或多次。 当这些语句是复合语句时,将按照顺序执行它们,除非遇到 break 语句或 continue 语句。
C++ 提供四个迭代语句 - 而 、为提供 、 为提供 ,为 提供基于范围的 语句。 每个循环访问,直到其终止表达式计算结果为零 (false) ,或者直到使用 语句强制循环 break 终止。 下表汇总了这些语句及其操作;后面各节详细讨论了它们。
语句 | 计算位置 | 初始化 | 增量 |
while | 循环的顶部 | 否 | 否 |
do | 循环的底部 | 否 | 否 |
for | 循环的顶部 | 是 | 是 |
range-based for | 循环的顶部 | 是 | 是 |
迭代语句的语句部分不能为声明。 但是,它可以是包含声明的复合语句。
(6)跳转语句。
这些语句可以立即将控制权转移到函数中的其他位置或从函数中返回控制权。
C++ 跳转语句执行控制的即时本地转换。
break;
continue;
return [expression];
goto identifier;
(7)声明语句。
声明将一个名称引入程序中。
C + + 程序由各种实体(如变量、函数、类型和命名空间)组成。 必须先 声明 其中的每个实体,然后才能使用这些实体。 声明为实体指定唯一的名称,以及其类型和其他特征的相关信息。 在 c + + 中,声明名称的点是它对编译器可见的点。 不能引用在编译单元中的某个后续点声明的函数或类。 在使用变量之前,应尽可能将变量声明为靠近。
(8)异常处理语句
3.2.4 返回类型
位于函数名前面的部分叫作函数返回类型,它描述的是从函数返回给调用它的函数的信息。在C语言中,省略返回类型相当于说函数的类型为int。然而,C++逐步淘汰了这种用法。void返回类型意味着函数不返回任何值。但是它不符合C++标准格式。
3.2.4 函数名
普通函数的函数名命名规划可以大小写混合,存取函数(accessors and mutators) 则要求与变量名匹配。
普通函数:
函数名以大写字母开头,每个单词首字母大写,没有下划线:AddTableEntry()。
存取函数:
存取函数要与存取的变量名匹配,这儿摘录一个拥有实例变量 age的类:
class MyClass
{
public:
...
int age ()
{
return age;
}
void set_age(int age)
{
age = age;
}
private:
int age;
};
为什么main()不能使用其它名称
通常,C++程序必须包含一个名为main()的函数,且只能有一个。在运行C++程序时,通常从main()函数开始执行。
3.2.5 函数参数
函数名后括号中的部分叫作形参列表(argument list)或参数列表(parameter list);它描述的是从调用函数传递给被调用的函数的信息。
通常,C++函数在调用另一个函数时,可以将信息传递给该函数。空括号意味着main()函数不接受任何信息,或者main()不接受任何参数。在括号中也可以使用关键字void明确地指出,函数不接受任何参数。在C++中,让括号空着与在括号中使用void等效(在C中,让括号空着意味着对是否接受参数保持沉默)。
3.2.6 返回值
C++函数可以给调用函数返回一个值,这个值叫作返回值(return value)。在这里,从关键字int可知,main()返回一个整数值。这个返回值不是必须的,如果编译器到达main()函数末尾时没有遇到返回语句,则认为main()函数以如下语句结尾:return 0; 这条隐含的返回语句只适用于main()函数,而不适用于其他函数。
通常情况下当函数执行到第1条return语句时,函数就结束运行并且将return后的变量值返回给函数调用者。主调函数继续执行调用函数之后的下一条语句。通常当函数返回值 为空(void)时,可以不需要return语句,函数的语句会依次执行到函数体的最后一条语句。
3.2.7 C++注释
注释是程序员为读者提供的说明,通常标识程序的一部分或解释代码的某个方面。编译器忽略注释。
行注释:// C++注释以双斜杠(//) 打头。
块注释(多行注释): /* 这是注释 */
在/*和*/注释内部,//字符没有特殊的含义。在//注释内,/*和*/字符也没有特殊的含义。因此,您可以在一种注释内嵌套另一种注释。块注释符(/*...*/)是不可以嵌套使用的。
快捷键:
Ctrl + shift + /
Ctrl + K + C / Ctrl + K + U
条件编译
#if 0 ... #endif 属于条件编译,0 即为参数。
我们还可以使用 #if 0 ... #endif 来实现注释,且可以实现嵌套,格式为:
#if 0
code
#endif // 0
你可以把 #if 0 改成 #if 1 来执行 code 的代码。
这种形式对程序调试也可以帮助,测试时使用 #if 1 来执行测试代码,发布后使用 #if 0 来屏蔽测试代码。
#if 后可以是任意的条件语句。
3.2.8 using namespace
3.2.8.1 namespace
namespace是命名空间有时也被称为名字空间、名称空间。它是一项C++特性。
语法格式为:
#include <conio.h>
#include <iostream>
namespace car
{
int model;
int length;
int width;
}
namespace plane
{
int model;
namespace size
{
int length;
int width;
}
}
namespace car
{
char* name;
}
namespace c = car;
int Time;
int main()
{
c::length = 3;
//width = 2;
plane::size::length = 70;
std::cout << "the length of plane is " << plane::size::length << "m." << std::endl;
std::cout << "the length of car is " << car::length << "m." << std::endl;
std::cout << "the length of c is " << c::length << "m." << std::endl;
int Time = 1996;
::Time = 1997;
std::cout << "Temp Time is " << Time << std::endl;
std::cout << "Outer Time is " << ::Time << std::endl;
using namespace plane;
model = 202;
size::length = 93;
std::cout << model << std::endl;
std::cout << size::length << std::endl;
return 0;
}
:: 是一个新符号,称为域解析操作符,在C++中用来指明要使用的命名空间。
名称空间是C++语言中为了解决编写大型程序时,多个厂商的独立C++代码在标识符命名过程中可能会发生冲突的一个解决方案。
命名空间是为防止名字冲突提供的一种控制机制,它由关键字namespace和命名空间的名字组成,它像变量、函数、类等一样,每个命名空间都有自己的作用域。
实际开发中,常常会出现命名空间嵌套的情况,在嵌套的命名空间中定义的只能在内层命名空间中有效,外层命名空间想要访问它必须在名字前添加限定符。
C++11新标准引入了一种新的嵌套命名空间叫内联命名空间,内联命名空间可以直接被外层的命名空间直接使用,内联命名空间的定义方式是在namespace前面加上inline关键字,后续使用该命名空间时就可以省略inline关键字。
注:
在C++语言编写的程序中,变量和函数等的作用范围是有一定限制的。比如,在函数体中定义的一个临时变量就不可以在函数体外使用。为了解决变量和函数等的作用范围,在C++语言中引入了名称空间的概念,并增加了关键字namespace和using。同时,在编写大型程序以及将多个厂商现有的代码组合起来的程序时更容易,它还有助于组织程序。
在一个名称空间中可以定义一组变量和函数,这些变量和函数的作用范围一致,可以将这些变量和函数称为这个名称空间的成员。
通过名称空间,可以在同一个文件中使用相同的变量名或函数名,只要它们属于不同的名空间。另外,名称空间可以使得代码操作具有相同名字但属于不同库的变量。而且,名称空间也可以提高C语言与C++语言的兼容性。
C++是在C语言的基础上开发的,早期的 C++还不完善,不支持命名空间,没有自己的编译器,而是将 C++ 代码翻译成C代码,再通过C编译器完成编译。这个时候的 C++ 仍然在使用C语言的库,stdio.h、stdlib.h、string.h 等头文件依然有效;此外 C++ 也开发了一些新的库,增加了自己的头文件,例如:
- iostream.h:用于控制台输入输出头文件。
- fstream.h:用于文件操作的头文件。
- complex.h:用于复数计算的头文件。
和C语言一样,C++ 头文件仍然以.h
为后缀,它们所包含的类、函数、宏等都是全局范围的。
后来 C++ 引入了命名空间的概念,计划重新编写库,将类、函数、宏等都统一纳入一个命名空间,这个命名空间的名字就是std。std 是 standard 的缩写,意思是“标准命名空间”。
但是这时已经有很多用老式 C++ 开发的程序了,它们的代码中并没有使用命名空间,直接修改原来的库会带来一个很严重的后果:程序员会因为不愿花费大量时间修改老式代码而极力反抗,拒绝使用新标准的 C++ 代码。
C++ 开发人员想了一个好办法,保留原来的库和头文件,它们在 C++ 中可以继续使用,然后再把原来的库复制一份,在此基础上稍加修改,把类、函数、宏等纳入命名空间 std 下,就成了新版 C++ 标准库。这样共存在了两份功能相似的库,使用了老式 C++ 的程序可以继续使用原来的库,新开发的程序可以使用新版的 C++ 库。
为了避免头文件重名,新版 C++ 库也对头文件的命名做了调整,去掉了后缀.h
,所以老式 C++ 的iostream.h
变成了iostream
,fstream.h
变成了fstream
。而对于原来C语言的头文件,也采用同样的方法,但在每个名字前还要添加一个c
字母,所以C语言的stdio.h
变成了cstdio
,stdlib.h
变成了cstdlib
。
需要注意的是,旧的 C++ 头文件是官方所反对使用的,已明确提出不再支持,但旧的C头文件仍然可以使用,以保持对C的兼容性。实际上,编译器开发商不会停止对客户现有软件提供支持,可以预计,旧的 C++ 头文件在未来数年内还是会被支持。
下面是我总结的 C++ 头文件的现状:
1) 旧的 C++ 头文件,如 iostream.h、fstream.h 等将会继续被支持,尽管它们不在官方标准中。这些头文件的内容不在命名空间 std 中。
2) 新的 C++ 头文件,如 iostream、fstream 等包含的基本功能和对应的旧版头文件相似,但头文件的内容在命名空间 std 中。
注意:在标准化的过程中,库中有些部分的细节被修改了,所以旧的头文件和新的头文件不一定完全对应。
3) 标准C头文件如 stdio.h、stdlib.h 等继续被支持。头文件的内容不在 std 中。
4) 具有C库功能的新C++头文件具有如 cstdio、cstdlib 这样的名字。它们提供的内容和相应的旧的C头文件相同,只是内容在 std 中。
可以发现,对于不带.h
的头文件,所有的符号都位于命名空间 std 中,使用时需要声明命名空间 std;对于带.h
的头文件,没有使用任何命名空间,所有符号都位于全局作用域。这也是 C++ 标准所规定的。
不过现实情况和 C++ 标准所期望的有些不同,对于原来C语言的头文件,即使按照 C++ 的方式来使用,即#include <cstdio>
这种形式,那么符号可以位于命名空间 std 中,也可以位于全局范围中,请看下面的两段代码。
1) 使用命名空间 std:
#include <cstdio>
int main(){
std::printf("http://c.biancheng.net\n");
return 0;
}
2) 不使用命名空间 std:
#include <cstdio>
int main(){
printf("http://c.biancheng.net\n");
return 0;
}
这两种形式在 Microsoft Visual C++和GCC下都能够编译通过,也就是说,大部分编译器在实现时并没有严格遵循C++标准,它们对两种写法都支持,程序员可以使用std也可以不使用。
第 1) 种写法是标准的,第 2) 种不标准,虽然它们在目前的编译器中都没有错误,但我依然推荐使用第 1) 种写法,因为标准写法会一直被编译器支持,非标准写法可能会在以后的升级版本中不再支持。
#include <conio.h>
#include <iostream>
namespace car
{
int model;
int length;
int width;
}
namespace plane
{
int model;
namespace size
{
int length;
int width;
}
}
namespace car
{
char* name;
}
namespace c = car;
int Time;
int main()
{
c::length = 3;
//width = 2;
plane::size::length = 70;
std::cout << "the length of plane is " << plane::size::length << "m." << std::endl;
std::cout << "the length of car is " << car::length << "m." << std::endl;
std::cout << "the length of c is " << c::length << "m." << std::endl;
int Time = 1996;
::Time = 1997;
std::cout << "Temp Time is " << Time << std::endl;
std::cout << "Outer Time is " << ::Time << std::endl;
using namespace plane;
model = 202;
size::length = 93;
std::cout << model << std::endl;
std::cout << size::length << std::endl;
return 0;
}
说明:
1)从上面可以看出,名称空间定义了一组变量和函数,它们具有相同的作用范围。对于不同的名称空间,可以定义相同的变量名或函数名,在使用的时候,只要在变量名或函数名前区分开不同的名称空间就可以了。
2)名称空间可以被嵌套定义,使用时要逐级对成员用名空间限定符: :来引用。
3)系统默认有一个全局名空间,它包含了所有的外部变量。这个名称空间没有名字,引用这个名称空间里的变量时要使用名称空间限定符: :,前面没有名字。在不使用名称空间的情况下,我们知道,不可以在不同文件中定义相同名字的外部变量,这是因为它们属于同一个全局名空间,名字不可以重复。
4)可以给名称空间取一个别名。一般别名是一个比较短的名字,来简化编程。
5)在原有定义好的名称空间的基础上,随时可以往里增加成员。
// UsingTest.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include <vector>
using namespace std;
// UINT和Uint都是unsigned int的别名
typedef unsigned int UINT;
using Uint = unsigned int;
using ULL = unsigned long long;
// using 定义别名模板
template<typename T>
using vec = vector<T>;
int main()
{
cout << "-----------using定义类型别名------------" << endl;
Uint uiTmpNum = 10;
ULL ullTmpMin = LLONG_MIN;
ULL ullTmpMax = LLONG_MAX;
cout << "uiTmpNum=" << uiTmpNum << endl;
cout << "ullTmpMin=" << ullTmpMin << endl;
cout << "ullTmpMax=" << ullTmpMax << endl;
cout << "-----------using定义别名模板使用------------" << endl;
vec<int> v = {0,1,2,3,4,5,6,7,8,9};
for (int i=0; i<v.size();++i)
{
cout << v.at(i) << endl;
}
std::cout << "Hello World!\n";
getchar();
}
3.2.8.2 using namespace
配合命名空间,对命名空间权限进行管理;using预编译器指令的主要功能是表明当前源代码文件使用的名称空间std。
using namespace std;//释放整个命名空间到当前作用域
using std::cout; //释放某个变量到当前作用域
利用using声明可以在引用命名空间成员时不必使用命名空间限定符::。
using std::cin; //必须每一个都有独立的using声明
using std::cout; using std::endl; //写在同一行也需要独立声明
需要注意的是每个名字需要独立的using声明。例如:
位于头文件的代码一般来说不应该使用using声明。因为头文件的内容会拷贝到所有引用它的文件中去,如果头文件里有某个using声明,那么每个使用了该头文件的文件就都会有这个声明,有可能产生名字冲突。
不同供应商的代码模块都拥有自己的名称空间,用户在使用这个模块时也需要明确标注自己使用的是哪一个厂商的代码模块。
程序访问命名空间的方式:
1、将using namespace std;放在函数定义之前,让文件中所有的函数都能够使用名称空间std中所有的元素
2、将using namespace std;放在特定的函数定义中,让该函数能够使用名称空间std中的所有元素
3、在特定的函数中使用类似using std::cout;这样的编译指令,而不是using namespace std;,让该函数能够使用指定的元素,如cout。
4、完全不使用编译指令using,而在需要使用命名空间std中的元素时,使用前缀std::。
3.2.9 C++代码风格
1、每条语句占一行;每一行代码字符数不超过 80个;
2、每个函数都有一个开始花括号和一个结束花括号,这两个括号各占一行;
3、函数中的语句都相对于花括号进行缩进;
4、与函数名称相关的圆括号周围没有空白;
5、缩进使用空格,每次缩进2个空格;使用空格进行缩进,不要在代码中使用tabs,设定编辑器将 tab 转为空格;