C++ 语言介绍

简介

C++ 是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化编程、面向对象编程和泛型编程。

C++ 是 C 的一个超集,事实上,任何合法的 C 程序都是合法的 C++ 程序,C++ 被认为是一种中级语言,它综合了高级语言和低级语言的特点。

C++ 是由 Bjarne Stroustrup 于 1979 年在新泽西州美利山贝尔实验室开始设计开发的。C++ 进一步扩充和完善了 C 语言,最初命名为带类的C,后来在 1983 年更名为 C++。

标准库

标准的 C++ 由三个重要部分组成:

  • 核心语言,提供了所有构件块,包括变量、数据类型和常量,等等。
  • C++ 标准库,提供了大量的函数,用于操作文件、字符串等。
  • 标准模板库(STL),提供了大量的方法,用于操作数据结构等。

面向对象程序设计

C++ 完全支持面向对象的程序设计,包括面向对象开发的三大特性:

  • 封装

    封装字面意思就是包装,信息隐藏,是利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个独立的实体,数据及数据处理细节被隐藏在内部,只提供对外的接口;

  • 继承

    继承能方便地复用以前的代码,但是是强耦合的;

  • 多态

    多态是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定的,即一个引用变量具体会指向那个类的实例对象,必须由程序运行期间才能决定。对应面向对象而言,多态可以分为编译时多态和运行时多态;其中编译时多态是静态的主要指方法的重载,运行时多态是动态的,它通过动态绑定来实现。

C++ 程序结构

示例简单代码:

1
2
3
4
5
6
7
8
9
#include <iostream>  // 头文件
using namespace std; // 告诉编译器使用 std 命名空间

// main 函数是程序开始执行的地方
int main()
{
cout << "Hello World"; // 输出 Hello World
return 0; // 终止 main( )函数,并向调用进程返回值 0
}

文件所在目录打开命令提示符,编译 & 执行 C++ 程序:

1
2
3
4
5
// 编译代码,并生成 a.out 可执行文件。
$ g++ hello.cpp
// 运行程序
$ ./a.out
Hello World

C++ 数据类型

使用编程语言进行编程时,需要用到各种变量来存储各种信息。变量保留的是它所存储的值的内存位置。这意味着,当您创建一个变量时,就会在内存中保留一些空间。

基本数据类型

C++ 七种基本基本的内置类型:

类型 关键字
布尔型 bool
字符型 char
整型 int
浮点型 float
双浮点型 double
无类型 void
宽字符型 wchar_t

一些基本类型可以使用一个或多个类型修饰符进行修饰:signed、unsigned、short、long;

下表显示了各种变量类型在内存中存储值时需要占用的内存,以及该类型的变量所能存储的最大值和最小值。

类型 位宽度 范围
char 1 个字节 -127 到 127 或者 0 到 255
unsigned char 1 个字节 0 到 255
signed char 1 个字节 -127 到 127
int 4 个字节 -2147483648 到 2147483648
unsigned int 4 个字节 0 到 4294967295
signed int 4 个字节 -2147483648 到 2147483647
short int 2 个字节 -32768 到 32767
unsigned short int Range 0 到 65,535
long int 4 个字节 -2,147,483,647 到 2,147,483,647
signed long int 4 个字节 与 long int 相同
unsigned long int 4 个字节 0 到 4,294,967,295
float 4 个字节 +/- 3.4e +/- 38 (~7 个数字)
double 8 个字节 +/- 1.7e +/- 308 (~15 个数字)
long double 8 个字节 +/- 1.7e +/- 308 (~15 个数字)
wchar_t 2 或 4 个字节 1 个宽字符

C++ 也允许定义各种其他类型的变量,比如枚举、指针、数组、引用、数据结构、类等等。

枚举类型

枚举类型声明一个可选的类型名称和一组标识符,用来作为该类型的值。带有零个或多个标识符可以被用来作为该类型的值。每个枚举数是一个枚举类型的常数。

创建枚举,需要使用关键字 enum。枚举类型的一般形式为:

1
enum enum-name { list of names } var-list;

在这里,enum-name 是枚举类型的名称。名称列表 { list of names } 是用逗号分隔的。

例如,下面的代码定义了一个颜色枚举,变量 c 的类型为 color。最后,c 被赋值为 “blue”。

1
2
enum color { red, green, blue } c;
c = blue;

默认情况下,第一个名称的值为 0,第二个名称的值为 1,第三个名称的值为 2,以此类推。但是,您也可以给名称赋予一个特殊的值,只需要添加一个初始值即可。例如,在下面的枚举中,green 的值为 5。

1
enum color { red, green=5, blue };

在这里,blue 的值为 6,因为默认情况下,每个名称都会比它前面一个名称大 1。

typedef 声明

可以使用 typedef 为一个已有的类型取一个新的名字。下面是使用 typedef 定义一个新类型的语法:

1
typedef type newname;

例如,下面的语句会告诉编译器,num 是 int 的另一个名称:

1
typedef int num;

用 num 创建了一个整型变量 age:

1
num age

C++ 变量作用域

作用域是程序的一个区域,一般来说有三个地方可以声明变量:

  • 在函数或一个代码块内部声明的变量,称为局部变量。
  • 在函数参数的定义中声明的变量,称为形式参数。
  • 在所有函数外部声明的变量,称为全局变量。

在函数或一个代码块内部声明的变量,称为局部变量。它们只能被函数内部或者代码块内部的语句使用。下面的实例使用了局部变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;

int main ()
{
// 局部变量声明
int a, b;
int c;

// 实际初始化
a = 10;
b = 20;
c = a + b;

cout << c;

return 0;
}

在所有函数外部定义的变量(通常是在程序的头部),称为全局变量。全局变量的值在程序的整个生命周期内都是有效的。

全局变量可以被任何函数访问。也就是说,全局变量一旦声明,在整个程序中都是可用的。下面的实例使用了全局变量和局部变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
using namespace std;

// 全局变量声明
int g;

int main ()
{
// 局部变量声明
int a, b;

// 实际初始化
a = 10;
b = 20;
g = a + b;

cout << g;

return 0;
}

注:在程序中,局部变量和全局变量的名称可以相同,但是在函数内,局部变量的值会覆盖全局变量的值。

当局部变量被定义时,系统不会对其初始化,您必须自行对其初始化。定义全局变量时,系统会自动初始化为下列值:int 0、char ‘\0’、float 0、double 0、pointer NULL。

定义常量

在 C++ 中,有两种简单的定义常量的方式:

  • 使用 #define 预处理器。
  • 使用 const 关键字。

#define 预处理器

具体示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
using namespace std;

#define LENGTH 10
#define WIDTH 5

int main()
{

int area;

area = LENGTH * WIDTH;
cout << area;
return 0;
}

const 关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
using namespace std;

int main()
{
const int LENGTH = 10;
const int WIDTH = 5;
int area;

area = LENGTH * WIDTH;
cout << area;
return 0;
}

注意:常量定义通常为大写字母形式。

与 const 相似的类型限定符还有volatilerestrict,区别:

  • const 类型的对象在程序执行期间不能被修改改变;
  • volatile 告诉编译器,变量的值可能以程序未明确指定的方式被改变;
  • restrict 修饰的指针是唯一一种访问它所指向的对象的方式。只有 C99 增加了新的类型限定符 restrict;

C++ 存储类

auto 存储类

auto 存储类是所有局部变量默认的存储类。

1
2
3
4
{
int mount;
auto int month;
}

上面的实例定义了两个带有相同存储类的变量,auto 只能用在函数内,即 auto 只能修饰局部变量。

register 存储类

register 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的 ‘&’ 运算符(因为它没有内存位置)。

1
2
3
{
register int miles;
}

寄存器只用于需要快速访问的变量,比如计数器。还应注意的是,定义 ‘register’ 并不意味着变量将被存储在寄存器中,它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制。

static 存储类

static 存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。

static 修饰符也可以应用于全局变量。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。

在 C++ 中,当 static 用在类数据成员上时,会导致仅有一个该成员的副本被类的所有对象共享。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>

// 函数声明
void func(void);

static int count = 10; /* 全局变量 */

main()
{
while(count--)
{
func();
}
return 0;
}
// 函数定义
void func( void )
{
static int i = 5; // 局部静态变量
i++;
std::cout << "i is " << i ;
std::cout << " and count is " << count << std::endl;
}

当上面的代码被编译和执行时,它会产生下列结果:

1
2
3
4
5
6
7
8
9
10
i is 6 and count is 9
i is 7 and count is 8
i is 8 and count is 7
i is 9 and count is 6
i is 10 and count is 5
i is 11 and count is 4
i is 12 and count is 3
i is 13 and count is 2
i is 14 and count is 1
i is 15 and count is 0

extern 存储类

extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 ‘extern’ 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。

当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。可以这么理解,extern 是用来在另一个文件中声明一个全局变量或函数。

extern 修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候,如下所示:

第一个文件:main.cpp

1
2
3
4
5
6
7
8
9
10
#include <iostream>

int count ;
extern void write_extern();

main()
{
count = 5;
write_extern();
}

第二个文件:support.cpp

1
2
3
4
5
6
7
8
#include <iostream>

extern int count;

void write_extern(void)
{
std::cout << "Count is " << count << std::endl;
}

openssl pkcs12 -clcerts -nokeys -out 5sing_apns_dev_cert.pem -in 5sing_apns_dev_cert.p12

openssl pkcs12 -clcerts -nokeys -out 5sing_apns_dev_key.pem -in 5sing_apns_dev_key.p12

openssl pkcs12 -clcerts -nokeys -out 5sing_apns_pro_cert.pem -in 5sing_apns_pro_cert.p12

openssl pkcs12 -clcerts -nokeys -out 5sing_apns_pro_key.pem -in 5sing_apns_pro_key.p12

cat 5sing_apns_dev_cert.pem 5sing_apns_dev_key.pem > 5sing_apns_dev.pem

cat 5sing_apns_pro_cert.pem 5sing_apns_pro_key.pem > 5sing_apns_pro.pem

openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert 5sing_apns_dev_cert.pem -key 5sing_apns_dev_key.pem

在这里,第二个文件中的 extern 关键字用于声明已经在第一个文件 main.cpp 中定义的 count。现在 ,编译这两个文件,如下所示:

1
$g++ main.cpp support.cpp -o write

这会产生 write 可执行程序,尝试执行 write,它会产生下列结果:

1
2
$./write
5

mutable 存储类

mutable 说明符仅适用于类的对象,这将在本教程的最后进行讲解。它允许对象的成员替代常量。也就是说,mutable 成员可以通过 const 成员函数修改。