C++是一种静态类型化、编译化、通用性、长度写敏感、自由格式编程语言,支持过程性、面向对象和泛型编程。 C++被视为一种中级语言,因为它包含高级语言和低级语言函数的组合。 C++由 Bjarne Stroustrup
于1979年在新泽西州默里山的贝尔实验室开发,作为C语言的加强版,最初命名为C类,但后来于1983年更名为C++。
在编译期间执行类型检查时,称为编程语言使用静态类型,而不是运行时。
C++
支持面向对象的编程,包含面向对象的开发四大支柱 |
- 封装
- 数据隐藏
- 继承
- 多态
标准C++由三个重要部分组成:
- 提供所有构建基块的核心语言,包含变量、数据类型和字面量等
C++
库,提供一组丰富的函数,用于操作文件、字符串等。- 标准模板库 (
STL
) 提供一组丰富的方法来操作数据结构等。
ANSI
标准是为了确保其 C++
;您为 Microsoft
编译器编写的代码将编译时没有错误, 使用 Mac
、UNIX
、Windows box
或 Alpha
上的编译器。 ANSI
标准已经稳定了一段时间,所有主要的C++制造商都支持 ANSI
标准。
学习时最重要的是C++
概念。 学习编程语言的目的是成为一个更好的程序员;也就是说,在设计和实施新系统以及维护旧系统方面更加有效。
c++ 支持多种编程风格,你可以使用 Fortran
风格,C
, Smalltalk
等风格。每种样式都可以有效地实现其目标,同时保持运行时和空间效率。
C++被几十万程序员用于本质上的每个应用程序领域。 C++被高度用于编写设备驱动程序和其他软件,这些软件依赖于在实时约束下直接操作硬件。 C++广泛用于教学和研究,因为它足够干净,足以成功教授基本概念。
任何使用过 Apple Macintosh
或运行 Windows
的 PC
的人都间接接触过 C++
,因为这些系统的主要用户界面是用C++。
这是一个C++编译器,将用于将源代码编译为最终的可执行程序。 大多数C++编译器都不关心您向源代码提供什么拓展名,但如果您没有指定其他方式,默认的形式是.cpp
。 最常用的和免费可用的编译器是 GNU C/C++
编译器,否则,如果您有相应的操作系统,则可以使用来自 HP
或 Solaris
的编译器。
如果您使用的是 Linux
或 UNIX
,则通过从命令行输入以下命令来检查系统上是否安装了 GCC
|
$ g++ -v
如果您已经安装了 GCC
,则它应该打印如下类似的消息 |
Using built-in specs.
Target: i386-redhat-linux
Configured with: ../configure --prefix=/usr .......
Thread model: posix
gcc version 4.1.2 20080704 (Red Hat 4.1.2-46)
如果未安装 GCC
,则您必须使用此处提供的详细说明 https://gcc.gnu.org/install/
如果您使用 Mac OS X,获取 GCC
的最简单方法是从 Apple
网站下载 Xcode
开发环境,并按照简单的安装说明进行操作。
Xcode
当前在 developer.apple.com/technologies/tools/
。
若要在 Windows
上安装 GCC
,您依赖安装 MinGW
。 要安装 MinGW
,请转到 MinGW
主页,www.mingw.org
,然后按照 MinGW
下载页面的链接进行。
下载最新版本的 MinGW
安装程序,该程序应命名为 MinGW-<版本>.exe。
安装 MinGW
时,至少必须安装 gcc
核心、gcc-g+
、binutils
和 MinGW
运行时(runtime
),但您可能希望安装更多。 将 MinGW
安装的 bin
子目录添加到 PATH
环境变量中,以便可以通过命令行中的简单名称指定这些工具。 安装完成后,您将能够从 Windows
命令行运行 gcc
、g+
、ar
、ranlib
、dlltool
和其他几个 GNU
工具。
当我们想到C++程序时,它可以定义为通过调用彼此的方法进行通信的对象的集合。 现在,让我们简要地了解一个类、对象、方法和即时变量意味着什么。
- 对象
对象具有状态和行为。示例:狗有状态 - 颜色,名称,品种以及行为 - 摇尾巴,吠,饮食。对象是类的实例。
- 类
类可以定义为模板/蓝图,用于描述其类型支持的对象的行为/状态。
- 方法
方法基本上是一种行为。类可以包含许多方法。它存在于编写逻辑、操作数据和执行所有操作的方法中。
- 即时变量
每个对象都有其唯一的实例变量集。对象的状态由分配给这些实例变量的值创建。
一个简单的c++ 代码文件示例:
请看该代码的几个部分的内容:
- C++语言定义了几个标头,其中包含对程序必要或有用的信息。
<iostream>
是必要的头部。 - 该行
using namespace std
;告诉编译器使用std命名空间。命名空间是C++中相对较新的函数。 - 下一行'// main()是程序开始执行的地方。 是C++中可用的单行注释。 单行注释以//开头,并在该行的结尾处停止。
int main()
行是程序执行开始的主要函数。- 下一行
cout <<"Hello World";
使消息"Hello World"显示在屏幕上。 - 下一行
return 0
;终止main()
函数并使它返回值 0 到调用进程。
确保 g++
在您的环境变量中,并且正在包含文件 hello.cpp
的目录中运行它。 您可以使用 makefile
编译 C/C++
程序。
在C++中,分号是语句终止符。 也就是说,每个单独的语句必须以分号结尾。 它表示一个逻辑实体的结尾。 例如,以下是三个不同的语句-
x = y;
y = y + 1;
add(x, y);
作用域是一组逻辑连接的语句,由开括号和闭括号包围。 例如:
{
cout << "Hello World"; // prints Hello World
return 0;
}
C++
无法将行尾识别为终止符。 因此,将语句放在一行中都没有关系。 例如-你也可以这么些:
x = y; y = y + 1; add(x, y);
C++标识符是用于标识变量,函数,类,模块或任何其他用户定义项的名称。 标识符以字母A
到Z
或a
到z
或下划线(_
)开头,后跟零个或多个字母,下划线和数字(0到9)。
C++
不允许在标识符内使用标点符号,例如@
,$
和%
。
C++
是区分长度写的编程语言。 因此,Manpower
和 manpower
是 C++
中的两个不同的标识符。
这是可接受的标识符的一些示例-
mohd zara abc move_name a_123
myname50 _temp j a23b9 retVal
以下列表显示了 C++
中的保留字。 这些保留字不得用作常量或变量或任何其他标识符名称。
asm else new this
auto enum operator throw
bool explicit private true
break export protected try
case extern public typedef
catch false register typeid
char float reinterpret_cast typename
class for return union
const friend short unsigned
const_cast goto signed using
continue if sizeof virtual
default inline static void
delete int static_cast volatile
do long struct wchar_t
double mutable switch while
dynamic_cast namespace template
一些字符具有另一种表示形式,称为三字符组序列。 三字符组是一个由三个字符组成的序列,代表一个字符,该序列始终以两个问号开头。 三字符组会延展到它们出现的任何位置,包含在字符串文字和字符文字中,在注释中以及在预处理程序指令中。
三字符组 | 置换 |
---|---|
??/ |
\ |
??' |
^ |
??( |
[ |
??) |
] |
??! |
|
??< |
{ |
??> |
} |
??- |
~ |
所有的编译器都不支持三字符组,并且由于其混乱的性质,不建议使用它们。
仅包含空格(可能带有注释)的行称为空白行,而 C++
编译器完全忽略它。 空格是 C++
中用来描述空格,制表符,换行符和注释的术语。 空格将语句的一部分与另一个部分分开,并使编译器能够识别语句中一个元素(例如int
)在何处结束以及下一个元素在何处开始。
语句1:
int age;
在上面的语句中,int
和 age
之间必须至少有一个空格字符(通常为空格),以便编译器能够区分它们。
语句2:
fruit = apples + oranges; // Get the total fruit
在上面的语句2中,在fruit
和=
号之间,或在=
号和apples
之间,不依赖空格字符,尽管出于可读性考虑,您可以自由地添加一些字符。
程序注释是可以包含在 C++
代码中的说明性语句。 这些注释可帮助任何人阅读源代码。 所有编程语言都允许使用某种形式的注释。
C++
支持单行和多行注释。
C++
编译器将忽略任何注释中可用的所有字符。
C++
注释以 /*
开头,以 */
结尾。 例如-
/* This is a comment*/
/* C++ comments can also
* span multiple lines
*/
注释也可以以 //
开头,延伸到该行的末尾。 例如-
编译以上代码时,它将忽略//
打印 Hello World
,最终可执行文件将产生以下结果-
Hello World
在 /*
和 */
注释中,//
字符没有特殊含义。 在 //
注释中,/*
和 */
没有特殊含义。 因此,您可以在另一种注释中"嵌套"一种注释。 例如-
/* Comment out printing of Hello World:
cout << "Hello World"; // prints Hello World
*/
用任何语言编写程序时,您依赖使用各种变量来存储各种信息。 变量不过是用于存储值的保留内存位置。 这意味着在创建变量时,您会在内存中保留一些空间。 您可能希望存储各种数据类型的信息,例如字符,宽字符,整数,浮点数,双浮点数,布尔值等。 操作系统根据变量的数据类型分配内存,并确定那些可以存储于剩余的内存。
C++
为程序员提供了丰富的内置以及用户定义的数据类型。 下表列出了七种基本的 C++
数据类型-
类型 | 关键字 |
---|---|
Boolean | bool |
Character | char |
Integer | int |
Floating point | float |
Double floating point | double |
Valueless | void |
Wide character | wchar_t |
可以使用一个或多个这些类型修饰符来修改几种基本类型-
- signed
- unsigned
- short
- long
下表显示了变量类型,在内存中存储值依赖的内存量以及此类变量中可以存储的最大值和最小值。
类型 | 典型位宽 | 典型范围 |
---|---|---|
char | 1byte | -127 to 127 or 0 to 255 |
unsigned char | 1byte | 0 to 255 |
signed char | 1byte | -127 to 127 |
int | 4bytes | -2147483648 to 2147483647 |
unsigned int | 4bytes | 0 to 4294967295 |
signed int | 4bytes | -2147483648 to 2147483647 |
short int | 2bytes | -32768 to 32767 |
unsigned short int | 2bytes | 0 to 65,535 |
signed short int | 2bytes | -32768 to 32767 |
long int | 8bytes | -2,147,483,648 to 2,147,483,647 |
signed long int | 8bytes | same as long int |
unsigned long int | 8bytes | 0 to 4,294,967,295 |
long long int | 8bytes | -(2^63) to (2^63)-1 |
unsigned long long int | 8bytes | 0 to 18,446,744,073,709,551,615 |
float | 4bytes | |
double | 8bytes | |
long double | 12bytes | |
wchar_t | 2 or 4 bytes | 1 wide character |
变量的长度可能不同于上表中显示的长度,具体取决于所使用的编译器和计算机。 以下是示例,它将在您的计算机上生成正确长度的各种数据类型:
此示例使用 endl
,它在每行之后插入一个换行符,并且 <<
操作符用于将多个值传递到屏幕。 我们还使用 sizeof()
运算符来获取各种数据类型的长度。 编译并执行上述代码后,将产生以下结果,该结果可能因计算机而异-
## windows 10 :
size of char is :1
size of int is :4
size of short int is :2
size of long int is :4
size of float is :4
size of double is :4
size of wchar_t is :4
您可以使用 typedef
为现有类型创建新名称。 以下是使用 typedef
定义新类型的简单语法-
typedef type newname;
例如,以下内容告诉编译器,feet
是 int
的另一个名称-
typedef int feet;
现在,以下声明完全合法,并创建一个称为 distance
的整数变量
feed distance;
枚举类型声明一个可选的类型名称,以及一组零个或多个可以用作该类型值的标识符。 每个枚举数都是一个常量,其类型为枚举。 创建枚举依赖使用关键字 enum
。 枚举类型的一般形式是-
enum enum-name { list of names } var-list;
在这里,枚举名称是枚举的类型名称。 名称列表以逗号分隔。 例如,以下代码定义了称为 color
的颜色和 color
类型的变量 c
的枚举。 最后,为c
分配值"blue
"。
enum color { red, green, blue } c;
c = blue;
默认情况下,第一个名称的值为 0
,第二个名称的值为 1
,第三个名称的值为 2
,依此类推。 但是您可以通过添加一个初始化程序来提供一个名称,一个特定的值。 例如,在下面的枚举中,绿色的值为 5
。
enum color { red, green = 5, blue };
在这里,blue
的值为6,因为每个名称都比其前一个大一个。
变量为我们提供了程序可以操纵的命名存储。
C++
中的每个变量都有一个特定的类型,该类型确定变量的内存长度和布局。 可以存储在该内存中的值的范围; 以及可以应用于该变量的一组操作。
变量的名称可以由字母,数字和下划线字符组成。 它必须以字母或下划线开头。 大写和小写字母是不同的,因为 C++
区分长度写-
如上一章所述,C++
中的变量具有以下基本类型-
类型 | 描述 |
---|---|
bool | 存储值 true 或 false 。 |
char | 通常是一个八位位组(一个字节)。这是整数类型。 |
int | 机器最自然的整数长度。 |
float | 单精度浮点值。 |
double | 双精度浮点值。 |
void | 表示没有类型。 |
wchar_t | 宽字符类型。 |
C++
还允许定义各种其他类型的变量,我们将在后续章节中介绍这些变量, 例如枚举,指针,数组,引用,数据结构和类。 下一节将介绍如何定义,声明和使用各种类型的变量。
变量定义告诉编译器在何处以及为变量创建多少存储空间。 变量定义指定一种数据类型,并包含一个或多个该类型变量的列表,如下所示:
type variable_list;
在这里,类型
必须是有效的 C++
数据类型, 包含 char
,w_char
,int
,float
,double
,bool
或任何用户定义的对象等, 并且 variable_list
可能包含一个或多个标识符名称,以逗号分隔。 一些有效的声明显示在这里-
当前行 int i,j,k;
都声明并定义变量 i,j
和 k
; 它表示编译器创建名为int
的 i,j
和 k
变量。 变量可以在其声明中进行初始化(分配初始值)。 初始化程序由一个等号和一个常量表达式组成,如下所示:
extern int d = 3, f = 5; // declaration of d and f.
int d = 3, f = 5; // definition and initializing d and f.
byte z = 22; // definition and initializes z.
char x = 'x'; // the variable x has the value 'x'.
对于没有初始化程序的定义:具有静态存储持续时间的变量隐式初始化为 NULL
(所有字节的值均为 0
); 所有其他变量的初始值是不确定的。
变量声明为编译器提供了保证,即存在一个具有给定类型和名称的变量,因此编译器可以继续进行进一步的编译,而无需有关该变量的完整详细信息。 变量声明仅在编译时具有其含义,编译器在程序链接时依赖实际的变量定义。
当您使用多个文件,并且在其中一个文件中定义变量时,变量声明很有用。在程序链接时,这些文件之一将可用。 您将在任何地方使用 extern
关键字声明变量。 尽管您可以在 C++
程序中多次声明变量,但是只能在文件,函数或代码块中定义一次。
请尝试以下示例,其中变量已在顶部声明,但已在 main
函数内部定义-
编译并执行上述代码后,将产生以下结果-
30
23.3333
相同的概念适用于函数声明,在声明函数时您要提供函数名称,而其实际定义可以在其他任何地方给出。 例如-
C++中有两种表达式-
- 左值:
引用内存位置的表达式称为"左值"表达式。 左值可能显示为分配的左侧或右侧。
- 右值:
术语"右值"是指存储在内存中某个地址处的数据值。 一个右值是一个不能为其赋值的表达式,这意味着一个右值可能出现在赋值的右手而不是左手。
变量是左值,因此可能出现在赋值的左侧。 数字文字是右值,因此可能无法赋值并且不能出现在左侧。 以下是有效的声明-
int g = 20;
但是以下内容不是有效的语句,并且会生成编译时错误-
10 = 20;
作用域是程序的一个区域,从广义上讲,可以在三个地方声明变量-
- 在函数或称为内部变量的块中,
- 在函数参数的定义中称为形式参数。
- 在所有函数之外,这称为全局变量。
- 在随后的章节中,我们将学习什么是函数及其参数。 在这里,让我们解释什么是局部变量和全局变量。
在函数或块内声明的变量是局部变量。 它们只能由该函数或代码块中的语句使用。 局部变量在其自身之外的函数中是未知的。 以下是使用局部变量的示例-
全局变量是在所有函数之外定义的,通常在程序顶部。 全局变量将在程序的整个生命周期内保持其值。 全局变量可以由任何函数访问。 也就是说,在声明之后,全局变量可在整个程序中使用。 以下是使用全局和局部变量的示例-
程序的局部变量和全局变量可以具有相同的名称,但是函数内局部变量的值将优先。 例如-
编译并执行上述代码后,将产生以下结果-
10
定义局部变量后,系统不会对其进行初始化,您必须自己对其进行初始化。 如下定义全局变量时,系统将自动初始化全局变量-
数据类型 | 初始化 |
---|---|
int | 0 |
char | '\0' |
float | 0 |
double | 0 |
pointer | NULL |
正确初始化变量是一种好的编程习惯,否则有时程序会产生意外的结果。
常数是指程序不能更改的固定值,它们称为字面量。 常量可以是任何基本数据类型,并且可以分为整数,浮点数字,字符,字符串和布尔值。 同样,常量的处理方式与常规变量相同,只是其值在定义后无法修改。
整型字面量字可以是十进制,八进制或十六进制常量。 前缀指定基数或基数:十六进制为0x
或0X
,八进制为0
,十进制为0
。 整型字面量也可以具有后缀,该后缀是U
和L
的组合,分别表示无符号
和长整数
。
后缀可以是大写或小写,并且可以是任何顺序。 这是整型字面量的一些例子-
浮点型字面量具有整数部分,小数点,小数部分和指数部分。 您可以用十进制或指数形式表示浮点文字。 在使用小数形式表示时,必须包含小数点和/或指数,或者在使用指数形式表示时,必须包含整数部分,小数或这两者。 有符号数由e
或E
引入。这是浮点文字的一些示例-
有两个布尔字面量,它们是标准C++
关键字的一部分-表示true
的true
值。 代表false
的false
值。 您不应认为true
的值等于1
,false
的值等于 0
。
字符字面量用单引号引起来。 如果字面量以L
开头(仅大写),则为宽字符字面量(例如L'x'),应存储在变量的 wchar_t
类型中。 否则,它是一个狭窄的字符字面量(例如'x'),可以存储在 char
类型的简单变量中。
字符文字可以是普通字符(例如'x'),转义序列(例如'\t')或通用字符(例如'\u02C0')。 在 C++
中,当某些字符前面带有反斜杠时,它们将具有特殊含义,并且用于表示换行符(\n
)或制表符( \t
)。
在这里,您有一些此类转义序列代码的列表-
转义序列 | 含义 |
---|---|
\ | 符号本身 |
' | 符号本身 |
" | 符号本身 |
? | 符号本身 |
\a | 警报或铃声 |
\b | 退格键 |
\f | 换页 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ooo | 八位数字(一到三位数) |
\xhh . . . | 十六进制的一位或多位数字 |
以下是显示一些转义序列字符的示例-
字符串字面量是用双引号引起来的。 字符串包含与字符文字相似的字符:纯字符,转义序列和通用字符。 您可以使用字符串字面量将长行分成多行,并使用空格分隔它们。 以下是一些字符串文字的示例。 这三种形式都是相同的字符串。
在 C++
中,有两种简单的方法可以定义常量-
- 使用
#define
预处理程序。 - 使用
const
关键字。
以下是使用 #define
预处理器定义常量的形式-
#define
[标识符][值]
以下示例详细解释-
编译并执行上述代码后,将产生以下结果-
50
您可以使用 const
前缀来声明具有特定类型的常量,如下所示-
以下示例详细解释-
C++
允许 char
,int
和 double
数据类型在它们前面有修饰符。 修饰符用于更改基本类型的含义,以使其更精确地适合各种情况的需求。
数据类型修饰符在这里列出
- signed 带符号
- unsigned 无符号
- long 长整型
- short 短整型
signed
,unsigned
,long
和 short
的修饰符可以应用于整数基类型。 另外,signed
和 unsigned
可以应用于 char
,而 long
可以应用于 double
。
signed
和 unsigned
的修饰符也可以用作 long
或 short
修饰符的前缀。 例如,unsigned long int
。
C++
允许使用简写形式来声明unsigned
,short
或long
。 您可以简单地使用unsigned
,short
或long
的单词,而不依赖使用 int
。 它自动暗示 int
。
例如,以下两个语句都声明无符号整数变量。
unsigned x;
unsigned int y;
要了解 C++
解释有符号
和无符号
整数修饰符的方式之间的区别,您应该运行以下简短程序-
运行该程序时,输出如下-
-15536 50000
上面的结果是因为将50,000
表示为短无符号整数的位模式被短解释为-15,536
。
修饰语 | 意义 |
---|---|
const | 程序无法在执行期间更改 const 类型的对象。 |
volatile | volatile 修饰符告诉编译器,可以通过程序未明确指定的方式来更改变量的值。 |
restrict | 最初,通过限制限定的指针是可以访问其指向的对象的唯一方法。仅 C99 添加了一个新类型的限定符,称为 strict 。 |
存储类定义 C++
程序中变量/或函数的范围(可见性)和生命周期。 这些说明符位于它们修改的类型之前。 有以下存储类,可以在 C++
程序中使用
- auto
- register
- static
- extern
- mutable
auto
存储类是所有局部变量的默认存储类
int mount;
auto int month;
上面的示例定义了两个具有相同存储类的变量,auto
只能在函数内使用,即局部变量。
register
存储类用于定义应存储在寄存器而不是RAM
中的局部变量。 这意味着该变量的最大长度等于寄存器的长度(通常是一个字),并且不能对其应用一元的'&'运算符(因为它没有存储位置)。
{
register int miles;
}
该寄存器仅应用于依赖快速访问的变量(例如计数器)。 还应注意,定义"寄存器"并不意味着该变量将存储在寄存器中。 这意味着它可能根据硬件和实现限制存储在寄存器中。
静态存储类指示编译器在程序的生命周期内保留局部变量,而不是在每次进入和离开作用域时都创建并销毁它。 因此,将局部变量设为静态允许它们在函数调用之间保持其值。 静态修饰符也可以应用于全局变量
。
完成此操作后,它将导致该变量的作用域仅限于声明该变量的文件。 在 C++
中,当对类数据成员使用 static
时,它将导致该类的所有对象仅共享该成员的一个副本。
extern
存储类用于提供对所有程序文件可见的全局变量的引用。 当您使用 extern
时,变量无法初始化,因为将变量名指向先前已定义的存储位置即可。
如果您有多个文件,并且定义了一个全局变量或函数(还将在其他文件中使用),则 extern
将在另一个文件中被使用,以引用已定义的变量或函数。 只是为了理解,extern
用于在另一个文件中声明全局变量或函数。
当有两个或更多文件共享相同的全局变量或函数时,最常用 extern
修饰符,如下所述。
在这里,extern
关键字用于声明另一个文件中的 count
。 现在按如下所示编译这两个文件-
$g++ main.cpp support.cpp -o write
下面将生成写可执行程序,尝试执行写并检查结果,如下所示:
$./write
5
可变说明符仅适用于类对象,本教程后面将对此进行讨论。 它允许对象的成员覆盖 const
成员函数。 也就是说,可变(mutable
)成员可以通过 const
成员函数进行修改。
运算符是一个符号,告诉编译器执行特定的数学或逻辑操作。 C++
内置丰富的运算符,并提供以下类型的运算符-
- 算术运算符
- 关系运算符
- 逻辑运算符
- 按位运算符
- 赋值运算符
- 杂项运算符
- 本章将逐一研究算术,关系,逻辑,按位,赋值和其他运算符。
C++语言支持以下算术运算符-
假设变量 A
持有 10
,变量 B
持有 20
,则-
运算符 | 描述 | 示例 |
---|---|---|
+ | 添加两个操作数 | A + B 将得到30 |
- | 用第一个减去第二个操作数 | A-B 将得到-10 |
* | 将两个操作数相乘 | A* B将得到200 |
/ | 用分母除以分子 | B/A 得 2 |
% | 模运算符,整数除以后的余数 | B%A将得出0 |
++ | 增量运算符,将整数值加1 | A++将得到11 |
- | 递减运算符,将整数值减1 | A--将得到9 |
C++语言支持以下关系运算符
假设变量A持有10,变量B持有20,则-
运算符 | 描述 | 示例 |
---|---|---|
== | 检查两个操作数的值是否相等,如果是,则条件为 true 。 |
(A==B)不正确。 |
! | =检查两个操作数的值是否相等,如果值不相等,则条件为 true 。 |
(A!= B)为真。 |
> | 检查左操作数的值是否大于右操作数的值,如果是,则条件为 true 。 |
(A> B)不正确。 |
< | 检查左操作数的值是否小于右操作数的值,如果是,则条件为 true 。 |
(A > =检查左操作数的值是否大于或等于右操作数的值,如果是,则条件为true。 (A> = B)不正确。 |
<= | 检查左操作数的值是否小于或等于右操作数的值,如果是,则条件为 true 。 |
(A <= B)是正确的。 |
C++语言支持以下逻辑运算符。
假设变量A持有1,变量B持有0,则-
运算符 | 描述 | 示例 |
---|---|---|
&& | 称为逻辑 与 运算符。如果两个操作数都不为零,则条件为true。 | (A && B)是错误的。 |
¦;¦; | 称为逻辑或运算符。如果两个操作数中的任何一个都不为零,则条件为 true 。 |
(A ¦;¦; B)为 true 。 |
! | 称为逻辑非运算符。用于反转其操作数的逻辑状态。如果条件为真,则逻辑非运算符将为假。 | !(A && B)是正确的。 |
按位运算符对位进行运算并执行逐位操作。 &
,|
和 ^
的真值表如下-
p | q | p & q | p ¦; q | p ^ q |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
下表列出了 C++
语言支持的按位运算符。假设变量A持有60,变量B持有13,则-
运算符 | 描述 | 示例 |
---|---|---|
& | 二叉AND运算符将两个结果都复制到结果中(如果两个操作数中都存在)。 | (A&B)将给出12,即 0000 1100 |
¦ | 如果任一操作数中存在二叉或运算符,则会对其进行复制。 | (A ¦ B)将得出61,即 0011 1101 |
^ | 如果在一个操作数中设置了该位,但不是在两个操作数中都设置了位,则二叉 XOR 运算符将复制该位。 |
(A ^ B)将得出49即0011 0001 |
〜 | 二叉补码运算符是一元的,并且具有"翻转"位的作用。 | (〜A)将给出2的补码形式的-61,即1100 0011,这是由于带符号的二叉数。 |
<< | 二叉左移运算符。左操作数的值向左移动右操作数指定的位数。 | << 2将得到240,即1111 0000 |
>> | 二叉右移运算符。左操作数的值向右移动右操作数指定的位数。 | A >> 2将得到15,即0000 1111 |
C++
语言支持以下赋值运算符-
运算符 | 描述 | 示例 |
---|---|---|
= | 简单赋值运算符,将值从右侧操作数分配到左侧操作数。 | C = A + B将A + B的值赋给C |
+= | 添加AND赋值运算符,它将右操作数添加到左操作数,并将结果分配给左操作数。 | C + = A等于C = C + A |
-= | 减去AND赋值运算符,它从左操作数中减去右操作数,并将结果分配给左操作数。 | C-= A等效于C = C-A |
*= | 与AND赋值运算符相乘,它将右操作数与左操作数相乘并将结果分配给左操作数。 | C* = A等于C = C* A |
/= | 除以AND赋值运算符,它将左操作数除以右操作数,并将结果分配给左操作数。 | C/= A等于C = C/A |
%= | 模AND赋值运算符,它使用两个操作数取模并将结果分配给左操作数。 | C%= A等于C = C%A |
<<= | 左移AND赋值运算符。 | C << = 2与C = C << 2相同 |
>>= | 右移AND赋值运算符。 | C >> = 2与C = C >> 2相同 |
&= | 按位与赋值运算符。 | C&= 2与C = C&2相同 |
^= | 按位异或和赋值运算符。 | C ^ = 2与C = C ^ 2相同 |
¦= | 按位包含(OR)和赋值运算符。 | C ¦ = 2与 C = C ¦ 2 相同 |
操作 | 描述 |
---|---|
sizeof | sizeof 运算符返回变量的长度。例如,sizeof(a) ,其中"a"是整数,将返回4。 |
Condition ? X : Y | 条件运算符(?)。如果Condition为true,则返回X的值,否则返回Y的值。 |
, | 逗号运算符表示要执行的一系列操作。整个逗号表达式的值是逗号分隔列表的最后一个表达式的值。 |
. (点) , -> (瘦箭头) |
成员运算符用于引用类,结构和联合的单个成员。 |
Cast | 强制转换运算符将一种数据类型转换为另一种数据类型。例如,int(2.2000)将返回2。 |
& |
指针运算符 & 返回变量的地址。例如&a; 将给出变量的实际地址。 |
* |
指针运算符* 是指向变量的指针。例如* var; 将指针指向变量var 。 |
类别 | 运算符 | 关联性 |
---|---|---|
后缀 | ()[]->。 | ++-- |
一元 | +-! 〜++--(type)*&sizeof |
从右到左 |
* |
/% |
从左到右 |
加法 | +- | 从左到右 |
移动 | 从左向右 | << >> |
关系 | <<=>> = | 从左到右 |
平等==!= | 从左到右 | |
按位与 | & |
从左到右 |
按位XOR | ^ |
从左到右 |
按位或 | ¦ | 左到右 |
逻辑AND | && | 从左到右 |
逻辑或 | ¦ ¦ | 左到右 |
条件 | ?: | 从右到左 |
分配 | = + =-=* =/=%= >> = << =&= ^ = ¦ = | 从右到左 |
逗号 | , | 从左到右 |
在某些情况下,您依赖多次执行一个代码块。 通常,语句是按顺序执行的:函数中的第一个语句首先执行,然后执行第二个,依此类推。 编程语言提供了各种控制结构,允许更复杂的执行路径。 循环语句使我们可以多次执行一个语句或一组语句,以下是大多数编程语言中循环语句的概述-
条件是否为true,为true 执行条件语句 。。。为 false 执行其他条件语句
C++
编程语言提供了以下类型的循环来处理循环需求。
while
循环 while_loopfor
循环 for_loopdo while
循环 do_while_loop- 嵌套循环 nested_loop
循环控制语句循环控制语句从其正常顺序更改执行。
当执行离开作用域时,在该作用域中创建的所有自动对象都将被销毁。
C++
支持以下控制语句。
-
break 语句 break_statement
-
continue 语句 continue_statement
-
goto 语句 goto_statement
-
无限循环 for_loop
- if 语句 if_statement
- if else 语句 if_else_statement
- switch 语句 switch_statement
- 嵌套语句 nested_statement
- 嵌套 switch 语句 nested_statement
函数是一起执行任务的一组语句。
每个C++
程序至少都有一个函数,即 main()
,所有最简单的程序都可以定义其他函数。
您可以将代码分成单独的函数。
如何在不同函数之间划分代码取决于您,但是从逻辑上来说,划分通常是使每个函数执行特定任务。
函数声明告诉编译器函数的名称,返回类型和参数。
函数定义提供函数的实际主体。
C++
标准库提供了程序可以调用的许多内置函数。
例如,函数strcat()
连接两个字符串,
函数memcpy()
将一个内存位置复制到另一位置,还有更多函数。
已知具有各种名称的函数,例如方法,子例程或过程等。
return_type function_name( parameter list ) {
body of the function
}
C++
函数定义由函数头和函数体组成。
这是函数的所有部分
- 返回类型-函数可能返回值。
return_type
是函数返回的值的数据类型。 某些函数执行依赖的操作而不返回值。 在这种情况下,return_type
是关键字void
。 - 函数名称-这是函数的实际名称。 函数名称和参数列表共同构成函数有符号。
- 参数-参数就像一个占位符。 调用函数时,将一个值传递给参数。 此值称为实际参数或自变量。 参数列表是指函数参数的类型,顺序和数量。 参数是可选的; 也就是说,一个函数可能不包含任何参数。
- 函数体-函数体包含定义函数函数的语句的集合。
函数声明告诉编译器函数名称以及如何调用函数。 该函数的实际主体可以单独定义。 函数声明包含以下部分-
return_type function_name( parameter list );
对于上面定义的函数max()
,下面是函数声明-
int max(int num1, int num2);
参数名称在函数声明中并不重要,仅它们的类型是必需的,因此以下也是有效的声明-
int max(int, int);
在一个源文件中定义一个函数并在另一个文件中调用该函数时,依赖函数声明。 在这种情况下,应在调用函数的文件顶部声明该函数。
创建 C++
函数时,您依赖定义函数的函数。
要使用函数,您必须调用或调用该函数。
当程序调用函数时,程序控制将转移到被调用函数。
被调用函数执行已定义的任务,并在执行 return
语句或达到其函数结尾的右括号时,将程序控制权返回给主程序。
要调用一个函数,您只依赖传递依赖的参数以及函数名,如果函数返回一个值,则可以存储返回的值。
例如-
如果函数要使用参数,则它必须声明接受参数值的变量。 这些变量称为函数的形式参数。 形式参数的行为类似于函数内部的其他局部变量,并在进入函数时创建并在退出时销毁。 调用函数时,有两种方法可以将参数传递给函数-
将参数传递给函数的按值调用方法会将参数的实际值复制到函数的形式参数中。
在这种情况下,对函数内部参数的更改不会对参数产生影响。
默认情况下,C++
使用按值调用来传递参数。
通常,这意味着函数内的代码无法更改用于调用函数的参数。
考虑如下的函数 swap()
定义。
将上述代码放到一个文件中,进行编译和执行后,将产生以下结果:
Before swap, value of a :100
Before swap, value of b :200
After swap, value of a :100
After swap, value of b :200
这表明尽管已在函数中更改了值,但这些外部的值没有更改。
通过将参数传递给函数的按指针调用方法会将参数的地址复制到形式参数中。
在函数内部,该地址用于访问调用中使用的实际参数。
这意味着对参数所做的更改会影响传递的参数。
为了通过指针传递值,参数指针与任何其他值一样传递给函数。
因此,相应地,您依赖像下面的函数 swap()
中一样将函数参数声明为指针类型,该函数交换其参数指向的两个整数变量的值。
现在,让我们通过指针传递值来调用函数swap()
,如以下示例所示:
swap_pointer swap_pointer_main
将参数传递给函数的按引用调用方法会将参数的引用复制到形式参数中。
在函数内部,引用用于访问调用中使用的实际参数。
这意味着对参数所做的更改会影响传递的参数。
为了通过引用传递值,参数引用与任何其他值一样传递给函数。
因此,相应地,您依赖像下面的函数 swap()
一样将函数参数声明为引用类型,
该函数交换其参数所指向的两个整数变量的值(地址的值)。
swap_reference swap_reference_main
将上述代码放到一个文件中,进行编译和执行后,将产生以下结果:
Before swap, value of a :100
Before swap, value of b :200
After swap, value of a :200
After swap, value of b :100
定义函数时,可以为每个最后一个参数指定默认值。 如果在调用函数时将相应的参数留为空白,则将使用此值。 这是通过使用赋值运算符并为函数定义中的参数赋值来完成的。 如果在调用函数时未传递该参数的值,则使用默认的给定值,但如果指定了值,则将忽略此默认值,而使用传递的值。 考虑以下示例-
function_param_default_value func_default_value
通常,当我们使用 Numbers
时,我们使用 原生数据类型;,如 int
、short
、long
、float
和double
等。在讨论数据类型时,已解释了数字数据类型、可能的值C++范围。
在定义C++您已经在前几章中给出的各种示例中定义了数字。下面是另一个综合示例,用于定义C++数字 |
C++
除了可以创建的各种函数外,C++
还包含一些有用的函数。
这些函数在标准 C
和C++
库中被称为内置函数。
这些函数可以包含在程序中,然后使用。
C++
具有丰富的数学运算,可以对各种数字执行。下表列出了一些有用的内置数学函数,这些函数在C++。 要利用这些函数,您依赖包含数学头文件 <cmath>
在许多情况下,您依赖生成一个随机数。
实际上,您依赖了解两个函数的随机数生成。
第一个是 rand()
,此函数将只返回一个伪随机数。
解决此问题的方法是首先调用 srand()
函数。
以下是生成几个随机数的简单示例。
本示例利用 time()
函数获取系统时间上的秒数,
随机 rand()
种子函数 |
C++提供了一个数据结构,即数组,
它存储相同类型元素的固定长度的顺序集合。
数组用于存储数据集合,
但将数组视为相同类型的变量集合通常更有用。
声明一个数组变量(如number[0]、number[1]、...和 number[99])而不是声明单个变量,
例如 number
和使用 number[0] 、 number[1] 和...,number[99]来表示单个变量。
数组中的特定元素由索引访问。
所有数组都由连续的内存位置组成。最低地址对应于第一个元素,最高地址对应于最后一个元素。
若要在C++中声明数组,程序员指定元素的类型和数组依赖的元素数,如下所示 |
type arrayName [ arraySize ];
这称为一维数组。数组长度必须是大于零的整数常量, 类型可以是任何有效的C++类型。 例如,若要声明一个名为双精度平衡的 10 元素数组,请使用此语句 |
double balance[10];
您可以一个一个的初始化C++个数组元素,或使用单个语句初始化,如下所示 |
double balance[5] = {1000.0, 2.0, 3.4, 17.0, 50.0};
大括号之间的值数 {} 不能大于我们为方括号之间的数组声明的元素数 [ ] 。 以下是一个用于分配数组单个元素的示例 — 如果省略数组的长度,将创建一个足够大的数组来保存初始化。因此,如果您编写 |
double balance[] = {1000.0, 2.0, 3.4, 17.0, 50.0};
您将创建与上一示例中完全相同的数组。
balance[4] = 50.0;
上述语句在数组中为 50.0 的值分配元素编号 5
。
具有第 4 个索引的数组将是第 5 个,
即最后一个元素,因为所有数组都有 0 作为其第一个元素的索引,
也称为基索引。
通过索引数组名称来访问元素。 这是通过将元素的索引放在数组名称后面的方括号内来完成的。 例如-
double salary = balance[9];
上面的语句将从数组中取出第10
个元素,并将值赋给salary
变量。
以下是一个示例,它将使用上述所有三个概念。
声明,赋值和访问数组-
该程序利用 setw()
函数格式化输出。
编译并执行上述代码后,将产生以下结果-
Element Value
0 100
1 101
2 102
3 103
4 104
5 105
6 106
7 107
8 108
9 109
Process finished with exit code 0
数组对C++很重要,应该依赖更多细节。
以下是几个重要概念,对于C++
程序员来说应该很清楚-
名称 | 描述 |
---|---|
多维数组 | C++支持多维数组。多维数组的最简单形式是二维数组。 |
数组指针 | 您可以通过简单地指定数组名称而无需任何索引来生成指向数组第一个元素的指针。 |
数组指针传递给函数 | 您可以通过指定不带索引的数组名称,将指向数组的指针传递给函数。 |
从函数返回数组 | c++ 允许函数返回数组 |
C++允许多维数组。 这是多维数组声明的一般形式-
type name[size1][size2]...[sizeN];
例如,以下声明创建了三维5。 10。 4个整数数组- 多维数组的最简单形式是二维数组。 本质上,二维数组是一维数组的列表。 要声明长度为x,y的二维整数数组,您将编写以下内容:
type arrayName [ x ][ y ];
其中 type
可以是任何有效的 C++
数据类型,而 arrayName
是有效的 C++
标识符。
可以将二维数组视为一个表,该表将具有 x
的行数和 y
的列数。
一个二维数组
因此,数组a中的每个元素都由形式为 a[i][j]
的元素名称来标识,其中a是数组的名称,而i和j是唯一标识a中每个元素的下标。
多维数组可以通过为每行指定括号内的值来初始化。 以下是具有3行的数组,每行有4列。
int a[3][4] = {
{0, 1, 2, 3} , /* initializers for row indexed by 0*/
{4, 5, 6, 7} , /* initializers for row indexed by 1*/
{8, 9, 10, 11} /* initializers for row indexed by 2*/
};
指示依赖行的嵌套括号是可选的。 以下初始化等效于先前的示例-
int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
访问二维数组元素通过使用下标(即数组的行索引和列索引)访问二维数组中的元素。 例如-
int val = a[2][3];
上面的语句将从数组的第三行获取第4个元素。
如上所述,您可以具有任意数量的维数组,尽管您创建的大多数数组可能都是一维或二维的。
除非您阅读了与 C++
指针相关的章节,否则您很可能不会理解本章。
因此,假设您对 C++
中的指针有一点了解,那么让我们开始:数组名称
是指向数组第一个元素的常量指针
。
因此,在声明中-
double balance[50];
balance
是指向&balance[0]
的指针,它是数组 balance
的第一个元素的地址。
因此,以下程序片段为 p
分配了 balance
的第一个元素的地址-
double*p;
double balance[10];
p = balance;
使用数组名称作为常量指针是合法的,反之亦然。
因此,*(balance + 4)
是访问balance[4]
数据的合法方法。
一旦将第一个元素的地址存储在p
中,就可以使用*p
,*(p + 1)
,*(p + 2)
等访问数组元素。
下面是显示上述所有概念的示例-
在上面的示例中,p
是指向 double
的指针,这意味着它可以存储 double
类型变量的地址。
一旦我们在 p
中有地址,那么 *p
将为我们提供在 p
中存储的地址处可用的值,如上例所示。
C++
不允许将整个数组作为参数传递给函数。
但是,可以通过指定不带索引的数组名称来将指针传递给数组。
如果要在函数中将一维数组作为参数传递,则必须以以下三种方式之一声明函数形式参数,
并且所有三种声明方法都将产生相似的结果,因为每种方法都告诉编译器整数指针在运行
被接收。
方式1:
形式参数作为指针如下:
void myFunction(int*param) {
.
.
.
}
方式2: 形式参数作为数组长度如下-
void myFunction(int param[10]) {
.
.
.
}
方式3:
形式参数作为未调整长度的数组,如下所示:
void myFunction(int param[]) {
.
.
.
}
现在,考虑以下函数,该函数将数组作为参数以及另一个参数,并基于传递的参数,返回通过数组的数字的平均值,如下所示:
正如您所看到的,就函数而言,数组的长度无关紧要,因为 C++
对形式参数不执行任何边界检查。
C++
不允许将整个数组作为函数的参数返回。
但是,您可以通过指定不带索引的数组名称来返回指向数组的指针。
如果要从函数返回一维数组,则必须声明一个返回指针的函数,如以下示例所示:
int* myFunction() {
.
.
.
}
要记住的第二点是,C++
不主张将局部变量的地址返回到函数外部,因此您必须将局部变量定义为静态变量。
现在,考虑以下函数,它将生成10个随机数并使用数组返回它们,并按如下所示调用此函数-
C++
提供以下两种类型的字符串表示形式:
C
风格的字符串。- 标准
C++
引入的字符串类类型。
C
风格的字符串起源于C语言,并在 C++
中继续受支持。
该字符串实际上是一维字符数组,以空字符'\0'结尾。
因此,以空字符结尾的字符串包含包含该字符串的字符,后跟一个空字符。
以下声明和初始化创建一个由单词"Hello"组成的字符串。
为了将空字符保留在数组的末尾,包含字符串的字符数组的长度比单词"Hello"中的字符数大一位。
char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
如果遵循数组初始化的规则,那么可以编写以下语句,如下所示:
char greeting[] = "Hello";
C++
支持广泛的函数来处理以 null
终止的字符串-
函数 | 描述 |
---|---|
strcpy(s1, s2); | 将字符串s2复制到字符串s1中。 |
strcat(s1, s2); | 将字符串s2连接到字符串s1的末尾。 |
strlen(s1); | 返回字符串s1的长度。 |
strcmp(s1, s2); | 如果s1和s2相同,则返回0;如果 s1 < s2,则返回小于0的数。 如果s1> s2,则返回大于0的数。 |
strchr(s1, ch); | 返回指向字符串s1中字符ch首次出现的指针。 |
strstr(s1, s2); | 返回一个指针,该指针指向字符串s1中第一次出现的字符串s2。 |
下面是一个简单的示例:
标准的 C++
库提供了一个字符串类类型,它支持上述所有操作,此外还具有更多函数。
让我们查看以下示例-
C++
指针简单易学。
有些 C++
任务使用指针更容易执行,而其他C++
任务(例如动态内存分配)如果没有它们就无法执行。
如您所知,每个变量都是一个内存位置,并且每个内存位置都定义了其地址,可以使用与号(&
)运算符访问该地址,
该符号表示内存中的地址。
请看以下内容,它将打印定义的变量的地址-
指针是一个变量,其值是另一个变量的地址。 像任何变量或常量一样,必须先声明一个指针,然后才能使用它。 指针变量声明的一般形式是-
type*var-name;
在这里,type
是指针的基本类型。
它必须是有效的C++
类型,并且 var-name
是指针变量的名称。
用于声明指针的星号与用于乘法的星号相同。
但是,在此语句中,星号用于将变量指定为指针。
以下是有效的指针声明-
int*ip; // pointer to an integer
double*dp; // pointer to a double
float*fp; // pointer to a float
char*ch // pointer to character
所有指针的值的实际数据类型(无论是整数,浮点数,字符还是其他形式)都是相同的,即表示内存地址的十六进制数字。 不同数据类型的指针之间的唯一区别是指针指向的变量或常量的数据类型。
在C++
中使用指针很少有重要的操作,我们将非常频繁地使用指针。
- (a)我们定义一个指针变量。
- (b)将变量的地址分配给指针。
- (c)最后访问指针变量中可用地址处的值。
这是通过使用一元运算符*
完成的,该运算符返回变量的值,该变量位于其操作数指定的地址处。
以下是利用这些操作的示例-
指针有许多但很简单的概念,它们对C++
编程非常重要。
以下是一些C++
程序员应该清楚的重要指针概念
名称 | 描述 |
---|---|
空指针 | C++ 支持空指针,它是在几个标准库中定义的值为零的常量。 |
指针运算 | 可以在指针上使用四种算术运算符:++,-,+,- |
指针与数组 | 指针和数组之间有密切的关系。 |
指针到指针 | C++ 允许您将指针放在指针上,依此类推。 |
将指针传递给函数 | 通过引用或地址传递参数都可以使被调用函数在调用函数中更改传递的参数。 |
函数的返回指针 | C++ 允许函数返回指向局部变量,静态变量和动态分配的内存的指针。 |
在没有确切地址要分配的情况下,将指针 NULL
分配给指针变量始终是一个好习惯。
这是在变量声明时完成的。
分配了 NULL
的指针称为空指针。
NULL
指针是在几个标准库(包含 iostream
)中定义的值为零的常量。
请看以下程序-
在大多数操作系统上,不允许程序访问地址0
处的内存,因为该内存是由操作系统保留的。
但是,存储器地址0
具有特殊的意义。
它指示指针不旨在指向可访问的存储位置。
但是按照惯例,如果指针包含空(零)值,则假定该指针不指向任何内容。
要检查空指针,可以使用以下if
语句
if(ptr) // succeeds if p is not null
if(!ptr) // succeeds if p is null
因此,如果为所有未使用的指针提供了 null
值,
并且避免使用 null
指针,则可以避免意外误用未初始化的指针。
很多时候,未初始化的变量包含一些垃圾值,因此调试程序变得困难。
如您所知,指针是一个地址,它是一个数值; 因此,您可以像处理数值一样对指针执行算术运算。 指针可以使用四种算术运算符:++,-,+和-为了理解指针算术,让我们考虑ptr是一个指向地址1000的整数指针。假定32位整数,则让 我们对指针执行以下算术运算-
ptr++
ptr
将指向位置 1004
,因为每次 ptr
递增时,它将指向下一个整数。
该操作会将指针移动到下一个存储位置, 而不会影响该存储位置的实际值。
如果 ptr
指向地址为 1000
的字符,
则上述操作将指向位置 1001
,因为将在 1001
处得到下一个字符。
我们更喜欢在程序中使用指针而不是数组,因为变量指针可以递增, 这与数组名不同,因为数组名是常量指针,因此不能递增。 以下程序递增变量指针以访问数组的每个后续元素-
可以通过使用关系运算符(例如==
,<
>
)来比较指针。
如果p1
和p2
指向彼此相关的变量(例如同一数组的元素),
则可以有意义地比较p1
和p2
。
以下程序通过增加变量指针来修改前面的示例,
只要它指向的地址小于或等于数组的最后一个元素的地址,
即 &var[MAX-1]
-
指针和数组密切相关。 实际上,指针和数组在许多情况下是可互换的。 例如,指向数组开头的指针可以通过使用算术指针或数组样式索引来访问该数组。
但是,指针和数组不能完全互换。 例如,考虑以下程序-
将指针运算符*
应用于var
是完全可以接受的,但是修改var
值是非法的。
原因是var
是一个常数,它指向数组的开头,不能用作l
值。
因为数组名称会生成一个指针常量,所以只要不修改它,它就仍可以在指针样式的表达式中使用。
例如,以下是为var[2]
赋值500
的有效语句-
*(var + 2) = 500;
在理解指针数组的概念之前,让我们考虑以下示例,该示例使用3个整数的数组-
在某些情况下,我们要维护一个数组,该数组可以存储指向 int
或 char
或任何其他可用数据类型的指针。
以下是指向整数的指针数组的声明-
您还可以使用指向字符的指针数组来存储字符串列表,如下所示:
指向指针的指针是多种间接形式或指针链的形式。 通常,指针包含变量的地址。 当我们定义指向指针的指针时,第一个指针包含第二个指针的地址,该地址指向包含实际值的位置,如下所示。
指向指针的变量必须这样声明。
这是通过在其名称前面放置一个额外的星号来完成的。
例如,以下是声明指向 int
类型的指针的指针的声明-
int**var;
当指向指针的指针间接指向目标值时,访问该值依赖两次应用星号运算符,如下面的示例所示-
C++
允许您将指针传递给函数。
为此,只需将函数参数声明为指针类型。
在一个简单的示例中,我们将一个无符号的长整型指针传递给一个函数,
并更改该函数内的值,该值会反映在调用函数中-
可以接受指针的函数,也可以接受数组,如以下示例所示:
正如我们在上一章中所看到的,C++
如何允许从函数返回数组,
类似于 C++
允许您从函数返回指针的方式。
为此,您必须声明一个返回指针的函数,如以下示例所示:
int* myFunction() {
.
.
.
}
要记住的第二点是,将局部变量的地址返回到函数外部并不是一个好主意,因此您必须将局部变量定义为静态变量。 现在,考虑以下函数,该函数将生成10个随机数,并使用表示指针的数组名称(即第一个数组元素的地址)返回它们。
引用变量是别名,即已经存在的变量的另一个名称。 使用变量初始化引用后,可以使用变量名或引用名来引用变量。
引用经常与指针混淆,但是引用和指针之间的三个主要区别是:
- 您不能具有
NULL
引用。 您必须始终能够假定引用已连接到合法存储。 - 一旦将引用初始化为一个对象,就不能将其引用更改为另一个对象。
- 指针可以随时指向另一个对象。
- 创建引用时,必须对其进行初始化。 指针可以随时初始化。
int i = 17;
我们可以如下声明 i
的参考变量。
int &r = i;
阅读这些声明中的&
作为引用
。
因此,将第一个声明读取为"r
是初始化为i
的整数引用",将第二个声明读取为"s
是初始化为d
的 double
引用"。
以下示例使用int
和double
上的引用-
引用通常用于函数参数列表和函数返回值。
因此,以下是与C++
参考相关的两个重要主题,
C++
程序员应该清楚了解它们-
我们已经讨论了如何使用指针实现按引用调用概念。
这是另一个使用C++
参考引用调用的示例-
passing_parameters_by_references
通过使用引用而不是指针,可以使C++
程序更易于阅读和维护。
C++
函数可以像返回指针一样以类似的方式返回引用。
当一个函数返回一个引用时,它返回一个隐式指针指向其返回值。
这样,可以在赋值语句的左侧使用函数。
例如,考虑这个简单的程序-
返回引用时,请注意所引用的对象不会超出作用域。
因此,返回对本地 var
的引用是不合法的。
但是,您始终可以返回静态变量的引用。
int& func() {
int q;
//! return q; // Compile time error
static int x;
return x; // Safe, x lives outside this scope
}
C++
标准库没有提供正确的日期类型。
C++
从C
继承了用于日期和时间操作的结构和函数。
要访问与日期和时间相关的函数和结构,
您依赖在C++
程序中包含头文件。
有四种与时间相关的类型:clock_t
,time_t
,size_t
和tm
。
类型-clock_t
,size_t
和time_t
能够将系统时间和日期表示为某种整数。
结构类型tm
以具有以下元素的C
结构形式保存日期和时间-
struct tm {
int tm_sec; // seconds of minutes from 0 to 61
int tm_min; // minutes of hour from 0 to 59
int tm_hour; // hours of day from 0 to 24
int tm_mday; // day of month from 1 to 31
int tm_mon; // month of year from 0 to 11
int tm_year; // year since 1900
int tm_wday; // days since sunday
int tm_yday; // days since January 1st
int tm_isdst; // hours of daylight savings time
}
以下是重要的函数,我们在C
或C++
中使用日期和时间时会使用它们。
所有这些函数都是标准C
和C++
库的一部分,
您可以通过参考下面提供的C++
标准库来检查其详细信息。
函数 | 描述 |
---|---|
time_t time(time_t*time); | 这将返回自1970年1月1日以来经过的秒数的系统当前日历时间。如果系统没有时间,则返回.1。 |
char* ctime(const time_t* time); | 这将返回一个指针,其格式为日月年年小时:分钟:秒年\n\0 |
struct tm* localtime(const time_t* time); | 这将返回一个指向本地时间的tm结构的指针。 |
clock_t clock(void); | 这将返回一个近似于调用程序已运行时间的值。如果时间不可用,则返回值.1。 |
char* asctime ( const struct tm* time ); | 这将返回一个指向字符串的指针,该字符串包含存储在时间指向的结构中的信息,该信息转换为以下格式:日月日期小时:分钟:秒年\n\0 |
struct tmgmtime(const time_ttime); | 这将以tm 结构的形式返回指向时间的指针。时间以协调世界时(UTC)表示,本质上是格林威治标准时间(GMT)。 |
time_t mktime(struct tm*time); | 这将返回与时间所指向的结构中找到的时间等效的日历时间。 |
double difftime ( time_t time2, time_t time1 ); | 此函数以秒为单位计算time1 和time2 之间的时差。 |
size_t strftime(); | 此函数可用于以特定格式格式化日期和时间。 |
假设您要以本地时间或世界标准时间(UTC
)检索当前系统日期和时间。
以下是实现相同目的的示例-
在C
或C++
中使用日期和时间时,tm
结构非常重要。
如上所述,该结构以C
结构的形式保存日期和时间。
大多数与时间相关的函数都使用tm
结构。
以下是利用各种与日期和时间相关的函数和tm
结构的示例
-在使用本章的结构时,我假设您对C
结构以及如何使用arrow->
运算符访问结构成员有基本的了解。
C++
标准库提供了广泛的输入/输出函数集,我们将在后续章节中看到。
本章将讨论C++
编程依赖的非常基本和最常见的I/O
操作。
C++
I/O
发生在流中,流是字节序列。
如果字节从键盘,磁盘驱动器或网络连接等设备流向主存储器,
则这称为输入操作,并且如果字节从主存储器流向显示屏,
打印机,磁盘驱动器等设备
或网络连接等,这称为输出操作。
以下是对C++
程序很重要的头文件-
头文件 | 函数与描述 |
---|---|
<iostream> | 该文件定义了cin ,cout ,cerr 和clog 对象,它们分别对应于标准输入流,标准输出流,未缓冲的标准错误流和缓冲的标准错误流。 |
<iomanip> | 该文件声明了对于使用所谓的参数化流操纵器(例如setw 和setprecision )执行格式化的I/O 有用的服务。 |
<fstream> | 该文件声明用于用户控制文件处理的服务。我们将在"文件和流"相关章节中对此进行详细讨论。 |
预定义对象cin
是istream
类的实例。
据说cin
对象已连接到标准输入设备,通常是键盘。
cin
与流提取运算符结合使用,该运算符写为>>
,比下面的示例中的符号大两个。
C++
编译器还确定输入值的数据类型,
并选择适当的流提取运算符以提取值并将其存储在给定变量中。
在单个语句中,可以多次使用流提取运算符>>
。
要请求多个基准,可以使用以下命令:
cin >> name >> age;
这将等效于以下两个语句-
cin >> name;
cin >> age;
预定义对象cerr
是ostream
类的实例。
据说cerr
对象已附加到标准错误设备上,
该设备也是一个显示屏,
但对象cerr
没有缓冲,
并且每次插入cerr
的流都会导致其输出立即出现。
cerr
也与流插入运算符结合使用,如以下示例所示。
预定义的对象 clog
是ostream
类的实例。
clog
对象被认为是附加到标准错误设备上的,
该设备也是一个显示屏,但是对象clog
已被缓冲。
这意味着每次插入阻塞都会导致其输出保留在缓冲区中,
直到缓冲区被填充或缓冲区被刷新为止。
clog
还可以与流插入运算符结合使用,如以下示例所示。
通过这些小示例,您将看不到cout
,cerr
和clog
的任何区别,但是在编写和执行大型程序时,区别变得明显。
因此,最好的做法是使用cerr
流显示错误消息,并在显示其他日志消息时使用clog
。
C/C++
数组允许您定义组合多个相同类型数据项的变量,但是结构是另一种用户定义的数据类型,它允许您组合不同类型的数据项。
假设您要跟踪图书馆中的书籍,则使用结构来表示记录。
您可能依赖跟踪每本书的以下属性-
- 标题
- 作者
- 主题
- 图书ID
要定义结构,必须使用struct
语句。
struct
语句为您的程序定义了一种新的数据类型,该数据类型具有多个成员。
struct
语句的格式是这样:
struct [structure tag] {
member definition;
member definition;
...
member definition;
} [one or more structure variables];
结构标记
是可选的,每个成员定义都是一个普通变量定义,例如int i
;
或 float f
;
或任何其他有效的变量定义。
在结构定义的最后,最后一个分号之前,您可以指定一个或多个结构变量,但它是可选的。
这是您声明Book
结构的方式-
struct Books {
char title[50];
char author[50];
char subject[100];
int book_id;
} book;
要获取结构的任何成员,我们使用成员访问运算符(.
)。
成员访问运算符被编码为结构变量名和我们希望访问的结构成员之间的句点。
您将使用struct
关键字定义结构类型的变量。
以下是解释结构用法的示例-
您可以通过与传递任何其他变量或指针非常相似的方式将结构作为函数参数传递。 您将以与上述示例相同的方式访问结构变量-
structures_as_function_arguments
您可以按照与定义任何其他变量的指针非常相似的方式定义指向结构的指针,如下所示
struct Books*struct_pointer;
现在,您可以将结构变量的地址存储在上面定义的指针变量中。
要找到结构变量的地址,请将&
运算符放在结构名称之前,如下所示:
struct_pointer = &Book1;
要使用指向该结构的指针访问该结构的成员,必须使用->
运算符,如下所示:
struct_pointer->title;
让我们使用结构指针重写上面的示例,希望这将使您容易理解概念-
有一种定义结构的简便方法,也可以"别名"创建的类型。 例如-
typedef struct {
char title[50];
char author[50];
char subject[100];
int book_id;
} Books;
现在,您可以直接使用Books
来定义Books
类型的变量,而无需使用struct
关键字。
以下是示例-
Books Book1, Book2;
您可以对非结构使用typedef
关键字,如下所示:
typedef long int*pint32;
pint32 x, y, z;
x,y和z都是长整整型数的指针。
C++
编程的主要目的是向C
编程语言添加面向对象,而类是C++
的主要函数,它支持面向对象的编程,通常称为用户定义类型。
一个类用于指定对象的形式,它将数据表示形式和用于将该数据处理到一个整齐的包中的方法结合在一起。
一个类中的数据和函数称为该类的成员。
定义类时,将为数据类型定义一个蓝图。
这实际上并没有定义任何数据,但确实定义了类名的含义,即,该类的对象将由什么组成,以及可以对该对象执行什么操作。
类定义以关键字class
开头,后跟类名。
和 class body
,用大括号括起来。
类定义之后必须是分号或声明列表。
例如,我们使用关键字类定义Box
数据类型,如下所示-
class Box {
public:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
关键字public
确定了紧随其后的类的成员的访问属性。
可以从类外部在类对象范围内的任何位置访问公共成员。
您还可以将类的成员指定为私有成员或受保护成员,我们将在小节中讨论。
类提供了对象的设计图,因此基本上是从类创建对象的。
我们使用与声明基本类型的变量完全相同的声明类型来声明类的对象。
以下语句声明类Box
的两个对象-
Box Box1; // Declare Box1 of type Box
Box Box2; // Declare Box2 of type Box
可以使用直接成员访问运算符(.
)访问类对象的公共数据成员。
让我们尝试以下示例以使事情变得清晰起来-
重要的是要注意,不能使用直接成员访问运算符(.
)直接访问私有成员和受保护成员。
我们将学习如何访问私有成员和受保护成员。
到目前为止,您已经对C++
类和对象有了一个非常基本的想法。
还有一些与C++
类和对象有关的有趣概念,
我们将在下面列出的各个小节中进行讨论-
到目前为止,您已经对C++
类和对象有了一个非常基本的想法。
还有一些与C++
类和对象有关的有趣概念,我们将在下面列出的各个小节中进行讨论-
让我们采用先前定义的类来使用成员函数访问类的成员,而不是直接访问它们-
class Box {
public:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
double getVolume(void);// Returns box volume
};
成员函数可以在类定义中定义,也可以使用作用域解析运算符:
-单独定义。
即使您不使用内联说明符,在类定义中定义成员函数也会声明该内联函数。
所以您可以如下定义Volume()
函数-
class Box {
public:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
double getVolume(void) {
return length* breadth* height;
}
};
如果愿意,可以使用作用域解析运算符(::
)在类外定义相同的函数,如下所示:
double Box::getVolume(void) {
return length* breadth* height;
}
在这里,唯一重要的一点是,您必须在::
运算符之前使用类名。
将在对象上使用点运算符(.
)调用成员函数,该成员函数将仅按以下方式操作与该对象有关的数据-
Box myBox; // Create an object
myBox.getVolume(); // Call member function for the object
让我们提出以上概念来设置和获取类中不同类成员的值-
数据隐藏是面向对象编程的重要函数之一,它可以防止程序的函数直接访问类类型的内部表示。
类成员的访问限制由类主体中标记为公共,私有和受保护的部分指定。
关键字 public
,private
和 protected
称为访问说明符。
一个类可以具有多个公共,受保护或私有标记的部分。
每个部分都保持有效,直到看到另一个部分标签或类主体的右右括号为止。
成员和类的默认访问权限为私有。
class Base {
public:
// public members go here
protected:
// protected members go here
private:
// private members go here
};
公共成员可以从 class
外部但在程序内的任何地方访问。
您可以在没有任何成员函数的情况下设置和获取公共变量的值,如以下示例所示:
私有成员变量或函数无法访问,甚至无法从类外部查看。
只有 class
和 友元函数 可以访问私有成员。
默认情况下,一个类的所有成员都是私有成员,例如,在以下类中,
宽度是私有成员,这意味着在标记成员之前,
将假定该成员为私有成员-
class Box {
double width;
public:
double length;
void setWidth( double wid );
double getWidth( void );
};
实际上,我们在私有部分定义数据,并在公共部分定义相关函数, 以便可以从类外部调用它们,如以下程序所示。
受保护的成员变量或函数与私有成员非常相似,但是它提供了另一个好处,即可以在称为派生类的子类中访问它们。
在下一章中,您将学习派生类和继承。
现在,您可以检查以下示例,其中我从父类 Box
派生了一个子类 SmallBox
。
下面的示例与上面的示例相似,此处width成员将可由其派生类SmallBox的任何成员函数访问。
类构造函数是类的特殊成员函数,只要我们创建该类的新对象,该构造函数便会执行。 构造函数将具有与类完全相同的名称,并且根本没有任何返回类型,甚至没有void。 构造函数对于为某些成员变量设置初始值非常有用。 以下示例解释了构造函数的概念-
默认构造函数没有任何参数,但是如果依赖,构造函数可以具有参数。 这有助于您在创建对象时为对象分配初始值,如以下示例所示-
如果使用参数化构造函数,则可以使用以下语法初始化字段-
Line::Line( double len): length(len) {
cout << "Object is being created, length = " << len << endl;
}
上面的语法等于下面的语法-
Line::Line( double len) {
cout << "Object is being created, length = " << len << endl;
length = len;
}
如果对于C类,您有多个字段X,Y,Z等要初始化,则use可以使用相同的语法并以逗号分隔字段,如下所示-
C::C( double a, double b, double c): X(a), Y(b), Z(c) {
....
}
析构函数是类的特殊成员函数,每当其类的对象超出范围或将delete
表达式应用于指向该类对象的指针时,都执行该析构函数。
析构函数的名称与以波浪号(〜
)为前缀的类的名称完全相同,并且既不能返回值,也不能采用任何参数。
析构函数对于在退出程序之前释放资源(例如关闭文件,释放内存等)非常有用。以下示例说明了析构函数的概念-
复制构造函数是一个构造函数,它通过使用先前创建的相同类的对象初始化对象来创建对象。 复制构造函数用于-
- 从另一个相同类型的对象初始化一个对象。
- 复制对象以将其作为参数传递给函数。
- 复制对象以从函数返回它。
如果没有在类中定义副本构造函数,则编译器本身将定义一个副本构造函数。 如果该类具有指针变量并具有一些动态内存分配,则必须具有副本构造函数。 复制构造函数的最常见形式如下所示:
classname (const classname &obj) {
// body of constructor
}
此处,obj
是对用于初始化另一个对象的对象的引用。
类的友元函数是在该类的作用域之外定义的,但是它有权访问该类的所有私有成员和受保护成员。
即使友元函数的原型出现在类定义中,友元也不是成员函数。
友元可以是函数,函数模板或成员函数,也可以是类或类模板,
在这种情况下,整个类及其所有成员都是友元。
要将函数声明为类的友元,请在类定义中的函数原型之前添加关键字friend
,如下所示:
class Box {
double width;
public:
double length;
friend void printWidth( Box box );
void setWidth( double wid );
};
若要将类ClassTwo
的所有成员函数声明为类ClassOne
的友元,请在类ClassOne
的定义中放置以下声明
friend class ClassTwo;
请看如下的程序:
C++
内联函数是类中常用的强大概念。
如果函数是内联函数,则编译器会在编译时调用该函数的每个点上放置该函数代码的副本。
对内联函数的任何更改都可能依赖重新编译该函数的所有客户端,
因为编译器将依赖再次替换所有代码,否则它将继续使用旧函数。
要内联函数,请将关键字inline
放在函数名称之前,
并在对函数进行任何调用之前定义函数。
如果定义的函数超过一行,则编译器可以忽略内联修饰符。
即使不使用内联说明符,类定义中的函数定义也是内联函数定义。
以下是一个示例,该示例利用内联函数返回两个数的最大值-
C++
中的每个对象都可以通过称为this
指针访问其自己的地址.
this
指针是所有成员函数的隐式参数。
因此,在成员函数内部,这可以用于引用调用对象。
友元函数没有 this
指针,因为友元不是类的成员。
只有成员函数具有 this
指针。
让我们尝试以下示例以了解 this
指针的概念-
我们可以使用 static
关键字定义 static
类。
当我们将一个类的成员声明为静态成员时,这意味着无论创建了多少个此类对象,静态成员只有一个副本。
静态成员由该类的所有对象共享。
如果没有其他初始化,则在创建第一个对象时,所有静态数据都将初始化为零。
我们不能将其放在类定义中,但是可以按照以下示例中的说明在类外部进行初始化,方法是使用作用域解析运算符::
来重新声明静态变量,以标识它属于哪个类。
让我们尝试以下示例以了解静态数据成员的概念-
通过将函数成员声明为静态成员,可以使其独立于类的任何特定对象。
即使不存在该类的对象,并且仅使用类名称和作用域解析运算符::
访问静态函数,也可以调用静态成员函数。
静态成员函数只能从类外部访问静态数据成员,其他静态成员函数和任何其他函数。
静态成员函数具有类作用域,并且无法访问该类的this
指针。
您可以使用静态成员函数来确定是否已创建该类的某些对象。
让我们尝试以下示例以了解静态函数成员的概念-
指向C++
类的指针的方法与指向结构的指针以及访问指向类的指针的成员的方法完全相同,
您可以使用成员访问运算符->
运算符,就如同对结构的指针一样。
同样,与所有指针一样,您必须在使用指针之前对其进行初始化。
让我们尝试以下示例,以了解指向类的指针的概念-
面向对象编程中最重要的概念之一就是继承。 继承使我们可以用另一个类来定义一个类,这使创建和维护应用程序变得更加容易。 这也提供了重用代码函数和快速实现时间的机会。 创建类时,程序员可以指定新类继承现有类的成员,而不必编写全新的数据成员和成员函数。 此现有类称为基类,而新类称为派生类。 继承的概念实现了一种关系。 例如,哺乳动物IS-A动物,狗IS-A哺乳动物以及狗IS-A动物等等。
一个类可以从多个类派生,这意味着它可以从多个基类继承数据和函数。 要定义派生类,我们使用类派生列表来指定基类。 一个类派生列表命名一个或多个基类,其格式为-
class [派生类]:[访问说明符] 基类
其中访问说明符是public
,protected
或 private
之一,而 cbase-class
是先前定义的类的名称。
如果未使用访问说明符,则默认情况下为私有。
考虑如下的基类 Shape
及其派生类 Rectangle
-
派生类可以访问其基类的所有非私有成员。 因此,派生类的成员函数不可访问的基类应在基类中声明为私有成员。 我们可以根据-谁可以通过以下方式访问它们来总结不同的访问类型-
访问 | public | protected | private |
---|---|---|---|
同类 | 可以 | 可以 | 可以 |
派生类 | 可以 | 可以 | 不可以 |
外部类 | 可以 | 不可以 | 不可以 |
派生类继承以下所有例外的所有基类方法
- 基类的构造函数,析构函数和副本构造函数。
- 基类的重载运算符。
- 基类的友元函数
从基类派生一个类时,可以通过公共,受保护或私有继承来继承基类。 继承的类型由访问说明符指定,如上所述。 我们几乎不使用受保护的继承或私有继承,但通常使用公共继承。 当使用不同类型的继承时,遵循以下规则-
- 公共继承-从公共基类派生一个类时,基类的公共成员成为派生类的公共成员,而基类的受保护成员成为派生类的受保护成员。 不能直接从派生类访问基类的私有成员,而可以通过调用基类的公共成员和受保护成员来访问。
- 受保护的继承-从受保护的基类派生时,基类的公共成员和受保护成员成为派生类的受保护成员。
- 私有继承-从私有基类派生时,基类的公共成员和受保护成员将成为派生类的私有成员。
一个C++
类可以从多个类中继承成员,这是扩展语法-
class derived-class: access baseA, access baseB....
如果访问权限是公共的,受保护的或私有的,并且将为每个基类提供访问权限,则访问权限将用逗号分隔,如上所示。 让我们尝试以下示例-
C++
允许您为同一范围内的函数名或运算符指定多个定义,
分别称为函数重载和运算符重载。
重载声明是在相同范围内以与先前声明的声明相同的名称声明的声明,除了两个声明具有不同的自变量且显然具有不同的定义(实现)。
调用重载的函数或运算符时,
编译器通过将用于调用函数或运算符的参数类型与定义中指定的参数类型进行比较,
来确定最合适的定义。
选择最合适的重载函数或运算符的过程称为重载解析。
在同一个作用域中,可以为同一个函数名具有多个定义。
函数的定义必须在参数列表中的参数类型和/或数量上彼此不同。
您不能重载仅在返回类型上有所不同的函数声明。
以下是使用相同函数 print()
打印不同数据类型的示例-
您可以重新定义或重载C++
中可用的大多数内置运算符。
因此,程序员也可以将运算符与用户定义的类型一起使用。
重载的运算符是具有特殊名称的函数:关键字"operator
",后跟所定义的运算符的符号。
像任何其他函数一样,重载的运算符具有返回类型和参数列表。
Box operator+(const Box&);
声明可用于添加两个Box
对象的加法运算符,
并返回最终的Box
对象。
大多数重载运算符可以定义为普通的非成员函数或类成员函数。
如果我们将上述函数定义为类的非成员函数,
则必须为每个操作数传递两个参数,如下所示:
Box operator+(const Box&, const Box&);
以下示例显示了使用成员函数进行重载的运算符的概念。 此处,对象作为参数传递,其属性将使用此对象访问, 将调用此操作符的对象可以使用此操作符进行访问,如下所述:
以下是可以重载的运算符列表-
+ | - | * | / | % | ^ |
& | ¦ | ~ | ! | , | = |
< | > | <= | >= | ++ | -- |
<< | >> | == | != | && | ¦¦ |
+= | -= | /= | %= | ^= | &= |
¦= | *= | <<= | >>= | [] | () |
-> | ->* | new | new [] | delete | delete [] |
以下是运算符的列表,不能重载-
:: | .* | . | ?: |
一元运算符对单个操作数进行运算, 以下是一元运算符的示例
- 增量(
++
)和减量(--
)运算符。 - 一元减(
-
)运算符。 - 逻辑非(
!
)运算符。
一元运算符在被调用的对象上进行操作,通常,该运算符出现在对象的左侧,
如!obj
,-obj
和++obj
一样,
但有时它们也可以用作后缀,就像obj++
或obj--
。
下面的示例说明如何在前缀和后缀用法中重载减号(-
)运算符。
二叉运算符有两个参数,以下是二叉运算符的示例。 您经常使用二叉运算符,例如加法(+)运算符,减法(-)运算符和除法(/)运算符。 下面的示例说明如何重载加法(+)运算符。 同样,您可以重载减法(-)和除法(/)运算符。
relational_operators_overloading
C++语言支持各种关系运算符,例如(<,>,<=,> =,==等),可用于比较C++内置数据类型。 您可以重载这些运算符中的任何一个,这些运算符可用于比较类的对象。 下面的示例说明了<运算符如何可以重载,以及以类似的方式可以重载其他关系运算符。
relational_operators_overloading
C++能够使用流提取运算符>>
和流插入运算符<<
输入和输出内置数据类型。
流插入和流提取运算符也可以重载,以执行用户定义类型(如对象)的输入和输出。
在此,使运算符重载函数成为该类的友元非常重要,因为它将在不创建对象的情况下被调用。
下面的示例说明提取运算符>>
和插入运算符<<
的方式。
input_output_operators_overloading
递增或递减运算符是C++ 中可用的两个重要的一元运算符。
increment_decrement_operators_overloading
您可以像其他运算符一样重载赋值运算符(=
),它可以像复制构造函数一样用于创建对象。
以下示例说明了如何重载赋值运算符。
assignment_operators_overloading
对于类类型的对象,函数调用operator()
可以重载。
重载()
时,您并没有创建调用函数的新方法。
而是要创建一个可以传递任意数量参数的运算符。
下面的示例说明如何重载函数调用operator()
。
function_call_operator_overloading
下标运算符[]
通常用于访问数组元素。
可以重载该运算符以增强C++
数组的现有函数。
下面的示例说明如何下标运算符[]
重载。
subscripting_operator_overloading
类成员访问运算符(->
)可以重载,但有点棘手。
它被定义为使类类型具有"类似指针"的行为。
运算符->
必须是成员函数。
如果使用,则其返回类型必须是可以应用的指针或类的对象。
通常将operator->
与指针取消引用运算符*
结合使用以实现"智能指针"。
这些指针的行为与普通指针相同,
不同之处在于它们在您通过它们访问对象时执行其他任务,
例如销毁指针或使用该指针指向另一个对象时自动删除对象。
可以将解引用运算符->
定义为一元后缀运算符。
也就是说,给定一个类-
class Ptr {
//...
X* operator->();
};
可以使用类Ptr
的对象以类似于使用指针的方式来访问类X的成员。
例如-
void f(Ptr p ) {
p->m = 10 ; // (p.operator->())->m = 10
}
语句p-> m
解释为(p.operator->(())-> m
。
使用相同的概念,以下示例说明了如何重载类访问运算符->。
class_member_access_operator_overloading
多态性一词意味着具有多种形式。
通常,当存在类的层次结构并且它们通过继承关联时,就会发生多态。
C++
多态性意味着对成员函数的调用将导致执行不同的函数,具体取决于调用该函数的对象的类型。
考虑以下示例,其中其他两个类派生了基类-
输出错误的原因是,编译器一次将函数area()
的调用设置为基类中定义的版本。
这称为函数调用的静态解析或静态链接-函数调用在程序执行之前是固定的。
有时也称为早期绑定,因为area()
函数是在程序编译期间设置的。
但是现在,让我们在程序中进行一些修改,
并在 Shape
类的area()
声明之前加上关键字 virtual
,使它看起来像这样-
class Shape {
protected:
int width, height;
public:
Shape(int a = 0, int b = 0) {
width = a;
height = b;
}
// pure virtual function
virtual int area() = 0;
};
=0
告诉编译器该函数没有主体,并且上面的虚函数将称为纯虚函数。
这次,编译器将查看指针的内容而不是指针的类型。因此,由于 tri
和 rec
类的对象的地址以*shape
存储,因此将调用相应的area()
函数。
如您所见,每个子类都有一个单独的函数area()
实现。这就是通常使用多态的方式。您具有具有相同名称,甚至相同参数的函数但具有不同实现的不同类。
虚函数是基类中使用关键字 virtual
声明的函数。在基类中定义虚拟函数,在派生类中定义另一个版本,则向编译器发出信号,表示我们不依赖此函数的静态链接。
我们要做的是根据要为其调用的对象的类型来选择要在程序中任何给定位置调用的函数。这种操作称为动态链接或后期绑定。
您可能希望在基类中包含一个虚函数,以便可以在派生类中对其进行重新定义以适合该类的对象,但是您无法在基类中为该函数提供有意义的定义。
我们可以将基类中的虚函数area()
更改为以下内容-
class Shape {
protected:
int width, height;
public:
Shape(int a = 0, int b = 0) {
width = a;
height = b;
}
// pure virtual function
virtual int area() = 0;
};
=0
告诉编译器该函数没有主体,并且上面的虚函数将称为纯虚函数。
数据抽象是指仅向外界提供基本信息并隐藏其背景详细信息,即在程序中表示依赖的信息而不显示详细信息。
数据抽象是一种编程(和设计)技术,它依赖于接口和实现的分离。
让我们以一个电视的真实示例为例,您可以打开和关闭电视,更改频道,调节音量以及添加诸如扬声器,VCR
和 DVD
播放器之类的外部组件,但是您不知道其内部细节,就是说,您不知道它是如何通过空中或通过电缆接收信号的,如何转换它们的信号,最后将它们显示在屏幕上。
因此,可以说电视将其内部实现与外部接口清楚地分开了,您可以在不了解其内部知识的情况下使用电源按钮,频道转换器和音量控制之类的接口进行播放。
在C++
中,类提供了较高级别的数据抽象。它们向外界提供了足够的公共方法来玩耍对象的函数并操纵对象数据(即状态),而实际上并不知道内部如何实现类。
例如,您的程序可以调用sort()
函数,而无需知道该函数实际用于对给定值进行排序的算法。实际上,排序函数的基础实现可能会在库的各个发行版之间发生变化,并且只要接口保持不变,您的函数调用仍将起作用。
在C++
中,我们使用类来定义我们自己的抽象数据类型( ADT
)。您可以使用ostream
类的 cout
对象将数据流传输到标准输出,如下所示:
#include <iostream>
using namespace std;
int main() {
cout << "Hello C++" <<endl;
return 0;
}
在这里,您不依赖了解 cout
如何在用户屏幕上显示文本。您只依赖知道公共接口,并且"cout
"的基础实现可以自由更改。
在C++
中,我们使用访问标签来定义类的抽象接口。一个类可能包含零个或多个访问标签-
带有公共标签的成员可以访问该计划的所有部分。类型的数据抽象视图由其公共成员定义。
使用私有标签定义的成员不能访问使用该类的代码。专用部分从使用该类型的代码中隐藏实现。
对于访问标签出现的频率没有限制。每个访问标签都指定后续成员定义的访问级别。指定的访问级别一直保持有效,直到遇到下一个访问标签或看到类主体的右右括号为止。
数据抽象提供了两个重要的优势-
保护类内部避免发生意外的用户级错误,这些错误可能会破坏对象的状态。
该类实现可能会随着时间的推移而不断变化,以响应不断变化的需求或错误报告,而无需更改用户级代码。
通过仅在类的私有部分中定义数据成员,类作者可以自由地对数据进行更改。如果实现发生更改,则仅需检查类代码以查看更改可能产生的影响。如果数据是公共的,则直接访问旧表示形式的数据成员的任何函数都可能会损坏。
在其中使用公共成员和私有成员实现类的任何C++程序都是数据抽象的一个示例。考虑以下示例-
上一类将数字相加,然后返回总和。公共成员- addNum
和 getTotal
是与外界的接口,用户依赖了解它们才能使用该类。私有成员总数是用户不依赖了解的,但类正常运行是必需的。
抽象将代码分为接口和实现。因此,在设计组件时,必须使接口独立于实现,这样,如果您更改基础实现,则接口将保持不变。
在这种情况下,无论使用这些接口的程序是什么,它们都不会受到影响,只依赖使用最新的实现进行重新编译。
所有C++
程序均由以下两个基本元素组成-
-
程序语句(代码)-这是执行动作的程序的一部分,它们称为函数。
-
程序数据-数据是受程序函数影响的程序信息。
封装是一种面向对象的编程概念,它将数据与操纵数据的函数绑定在一起,并确保不受外界干扰和滥用。数据封装导致了重要的 OOP
概念:数据隐藏。
数据封装是捆绑数据的机制,使用它们的函数和数据抽象是仅公开接口并向用户隐藏实现细节的机制。
C++
通过创建称为类的用户定义类型来支持封装和数据隐藏的属性。我们已经研究过,一个 class
可以包含private
,受保护的成员和公共成员。默认情况下,一个类中定义的所有项目都是私有的。例如-
class Box {
public:
double getVolume(void) {
return length* breadth* height;
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
变量的长度,宽度和高度是私有的。这意味着它们只能由 Box
类的其他成员访问,而不能由程序的任何其他部分访问。这是实现封装的一种方式。
要使类的各个部分公开(即,程序的其他部分可以访问),则必须在 public
关键字之后声明它们。程序中的所有其他函数都可以访问在公共说明符之后定义的所有变量或函数。
使一个类成为另一个类的友元可以暴露实现细节并减少封装。理想的做法是使每个类的尽可能多的细节对所有其他类都隐藏。
在其中使用公共成员和私有成员实现类的任何C++程序都是数据封装和数据抽象的一个示例。考虑以下示例-
上一类将数字相加,然后返回总和。公共成员addNum和getTotal是与外界的接口,用户依赖了解它们才能使用该类。私人成员总数是对外界隐藏的东西,但对于班级正常运转是必需的。
我们大多数人都学会了默认情况下将类成员设为私有,除非我们确实依赖公开它们。那只是很好的封装。
这最常应用于数据成员,但同样适用于所有成员,包含虚拟函数。
接口描述了 C++
类的行为或函数,而无需承诺该类的特定实现。
C++
接口是使用抽象类实现的,这些抽象类不应与数据抽象混淆,数据抽象是一种将实现细节与关联数据分开的概念。
通过将至少一个函数声明为纯虚函数,可以使一个类抽象。通过在声明中放置"=0
"来指定纯虚函数,如下所示:
class Box {
public:
// pure virtual function
virtual double getVolume() = 0;
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
抽象类(通常称为 ABC
)的目的是提供其他类可以从中继承的适当基类。抽象类不能用于实例化对象,而只能用作接口。尝试实例化抽象类的对象会导致编译错误。
因此,如果依赖实例化 ABC
的子类,则它必须实现每个虚函数,这意味着它支持 ABC
声明的接口。未能重写派生类中的纯虚函数,然后尝试实例化该类的对象,则是编译错误。
可以用来实例化对象的类称为具体类。
考虑以下示例,其中父类提供了到基类的接口,以实现名为getArea()
的函数-
您可以看到抽象类如何根据getArea()定义接口,其他两个类实现了相同的函数,但是使用了不同的算法来计算特定于形状的面积。
面向对象的系统可能会使用抽象基类来提供适用于所有外部应用程序的通用标准化接口。然后,通过从该抽象基类继承,可以形成操作类似的派生类。
外部应用程序提供的函数(即公共函数)在抽象基类中作为纯虚拟函数提供。这些纯虚函数的实现在对应于应用程序特定类型的派生类中提供。
即使定义了系统,该体系结构也允许将新的应用程序轻松添加到系统中。
到目前为止,我们一直在使用 iostream
标准库,该库提供 cin
和 cout
方法,分别用于从标准输入读取和写入标准输出。
本教程将教您如何从文件读取和写入。这依赖另一个称为 fstream
的标准C++库,该库定义了三种新的数据类型-
数据类型 | 描述 |
---|---|
ofstream | 此数据类型表示输出文件流,用于创建文件和向文件写入信息。 |
ifstream | 此数据类型表示输入文件流,用于从文件中读取信息。 |
fstream | 此数据类型通常表示文件流,并且具有 ofstream 和 ifstream 的函数,这意味着它可以创建文件,向文件写入信息以及从文件读取信息。 |
要在C++中执行文件处理,头文件和必须包含在C++源文件中。
必须先打开文件,然后才能读取或写入文件。 ofstream
或 fstream
对象均可用于打开文件进行写入。而且 ifstream
对象仅用于读取目的而打开文件。
以下是open()
函数的标准语法,该函数是 fstream
,ifstream
和 ofstream
对象的成员。
void open(const char*filename, ios::openmode mode);
在这里,第一个参数指定要打开的文件的名称和位置,而open() 成员函数的第二个参数定义打开文件的模式。
模式标记 | 说明 |
---|---|
ios::app | 追加模式。该文件的所有输出都将附加到末尾。 |
ios::ate | 打开文件以输出,并将读/写控件移到文件末尾。 |
ios::in | 打开文件进行读取。 |
ios::out | 打开文件进行写入。 |
ios::trunc | 如果文件已经存在,则在打开文件之前其内容将被截断。 |
您可以通过对它们进行或运算来组合两个或多个这些值。例如,如果您想以写入模式打开文件并在已经存在的情况下要截断它,则语法如下-
ofstream outfile;
outfile.open("file.dat", ios::out | ios::trunc );
类似地,您可以打开文件以进行读写,如下所示:
fstream afile;
afile.open("file.dat", ios::out | ios::in );
####关闭文件
C++
程序终止时,它会自动刷新所有流,释放所有分配的内存并关闭所有打开的文件。但是,程序员应在程序终止前关闭所有打开的文件,这始终是一种好习惯。
以下是 close()
函数的标准语法,该函数是 fstream
, ifstream
和 ofstream
对象的成员。
void close();
在执行 C++
编程时,您可以使用流插入运算符( <<
)将信息从程序写入文件中,就像您使用该运算符将信息输出到屏幕上一样。唯一的区别是您使用了一个 ofstream
或 fstream
对象而不是 cout
对象。
您可以使用流提取运算符(>>
)将文件中的信息读取到程序中,就像使用该运算符从键盘上输入信息一样。唯一的区别是您使用了 ifstream
或 fstream
对象而不是 cin
对象。
以下是C++程序,该程序以读写模式打开文件。将用户输入的信息写入名为 afile.dat
的文件后,程序将从文件中读取信息并将其输出到屏幕上-
上面的示例使用了cin
对象中的其他函数,例如getline()
函数从外部读取行,而ignore()
函数忽略先前的 read
语句留下的多余字符。
istream
和 ostream
均提供用于重新定位文件位置指针的成员函数。这些成员函数是 istream
的seekg ("seek get")
和 ostream
的 seekp ("seek put")
。
seekg
和 seekp
的参数通常是一个长整数。可以指定第二个参数来指示搜索方向。查找方向可以是ios::beg
(默认值),用于相对于流的开头进行定位; ios::cur
,用于相对于流中的当前位置进行定位;或ios::end
,用于相对于流的末尾进行流定位。
文件位置指针是一个整数值,用于指定文件中从文件起始位置开始的字节数。定位"获取"文件位置指针的一些示例是-
// position to the nth byte of fileObject (assumes ios::beg)
fileObject.seekg( n );
// position n bytes forward in fileObject
fileObject.seekg( n, ios::cur );
// position n bytes back from end of fileObject
fileObject.seekg( n, ios::end );
// position at end of fileObject
fileObject.seekg( 0, ios::end );
例外是在程序执行期间出现的问题。 C++异常是对程序运行时出现的异常情况的一种响应,例如试图除以零。
异常提供了一种将控制权从程序的一部分转移到另一部分的方法。 C++异常处理基于三个关键字建立:try
,catch
和 throw
。
-
throw
-问题出现时,程序将引发异常。这是使用throw
关键字完成的。 -
catch
-程序在要处理问题的程序中的某个位置使用异常处理程序捕获异常。catch
关键字指示捕获异常。 -
try
-try块标识将为其激活特定异常的代码块。其次是一个或多个捕获块。
假设一个块会引发异常,则方法使用try和catch关键字的组合来捕获异常。在可能产生异常的代码周围放置了一个 try
/catch块
。 try
/catch
块中的代码称为受保护的代码,使用try/catch的语法如下-
try {
// protected code
} catch( ExceptionName e1 ) {
// catch block
} catch( ExceptionName e2 ) {
// catch block
} catch( ExceptionName eN ) {
// catch block
}
您可以列出多个 catch
语句以捕获不同类型的异常,以防在不同情况下try块引发多个异常。
可以使用 throw
语句在代码块内的任何位置抛出异常。 throw
语句的操作数确定异常的类型,可以是任何表达式,表达式结果的类型确定抛出的异常的类型。
以下是发生除以零的条件时引发异常的示例-
double division(int a, int b) {
if( b==0 ) {
throw "Division by zero condition!";
}
return (a/b);
}
try
块之后的 catch
块捕获任何异常。您可以指定要捕获的异常类型,这取决于关键字catch后面括号中的异常声明。
try {
// protected code
} catch( ExceptionName e ) {
// code to handle ExceptionName exception
}
上面的代码将捕获 ExceptionName
类型的异常。如果要指定 catch
块应处理在 try
块中引发的任何类型的异常,则必须在括起异常声明的括号之间放置省略号...,如下所示-
try {
// protected code
} catch(...) {
// code to handle any exception
}
以下是一个示例,该示例引发了一个被零除的异常,我们将其捕获在 catch
块中。
由于我们引发了const char*
类型的异常,因此在捕获此异常时,我们必须在catch块中使用const char*
。如果我们编译并运行以上代码,这将产生以下结果-
C++提供了在中定义的标准异常列表,我们可以在程序中使用它们。这些按以下所示的父子类层次结构排列-
异常 | 描述 |
---|---|
std::exception | 所有标准C++异常的异常和父类。 |
std::bad_alloc | 可以通过 new 抛出 |
std::bad_cast | 这可以由 dynamic_cast 抛出。 |
std::bad_exception | 这对于处理C++程序中的意外异常很有用。 |
std::bad_typeid | 可以由 typeid 抛出。 |
std::logic_error | 从理论上讲,可以通过阅读代码来检测异常。 |
std::domain_error | 使用数学上无效的域时,将引发异常。 |
std::invalid_argument | 由于参数无效而引发此错误。 |
std::length_error | 当创建太大的std::string 时抛出此错误。 |
std::out_of_range | 这可以通过'at '方法抛出,例如std::vector 和std:: bitset<>::operator[]() 。 |
std::runtime_error | 理论上,通过读取代码无法检测到异常。 |
std::overflow_error | 如果发生数学上的溢出,则会抛出此错误。 |
std::range_error | 当您尝试存储超出范围的值时,就会发生这种情况。 |
std::underflow_error | 如果发生数学下溢,则会抛出此错误。 |
您可以通过继承和覆盖异常类函数来定义自己的异常。以下是示例,显示了如何使用std::exception
类以标准方式实现自己的异常-
在此,what()
是异常类提供的公共方法,并且已被所有子异常类覆盖。这将返回异常原因。
充分了解动态内存在C++中的实际工作方式对于成为一名优秀的C++程序员至关重要。您的C++程序中的内存分为两部分-
-
堆栈-函数内部声明的所有变量将占用堆栈中的内存。
-
堆-这是程序的未使用内存,可用于在程序运行时动态分配内存。
很多时候,您不会事先知道依赖在定义的变量中存储特定信息的内存量,并且可以在运行时确定依赖内存的长度。
您可以使用C++中的特殊运算符在运行时为给定类型的变量在堆中分配内存,该运算符返回分配的空间的地址。该运算符称为新运算符。
如果您不再依赖动态分配的内存,则可以使用 delete
运算符,该运算符将取消分配由 new
运算符先前分配的内存。
使用以下通用语法可以使用 new
运算符为任何数据类型动态分配内存。
new data-type;
此处,数据类型可以是任何内置数据类型,包含数组,也可以是任何用户定义的数据类型,包含类或结构。让我们从内置数据类型开始。例如,我们可以定义一个指针来输入double
类型,然后请求在执行时分配内存。我们可以使用带有以下语句的new运算符来做到这一点-
double* pvalue = NULL; // Pointer initialized with null
pvalue = new double; // Request memory for the variable
如果空闲存储已用完,则可能未成功分配内存。因此,优良作法是检查 new
运算符是否返回 NULL
指针并采取以下适当的操作-
double* pvalue = NULL;
if( !(pvalue = new double )) {
cout << "Error: out of memory." <<endl;
exit(1);
}
C语言中的 malloc()
函数仍然存在于C++中,但是建议避免使用malloc()函数。与malloc()相比,new的主要优点是new不仅分配内存,而且还构造了C++的主要目的的对象。
无论何时,当您感觉不再依赖动态分配的变量时,都可以使用"删除"运算符释放它在免费存储区中占用的内存,如下所示:
delete pvalue; // Release memory pointed to by pvalue
考虑您要为一个字符数组(即20个字符的字符串)分配内存。使用与上面相同的语法,可以动态分配内存,如下所示。
char* pvalue = NULL; // Pointer initialized with null
pvalue = new char[20]; // Request memory for the variable
要删除我们刚刚创建的数组,语句将如下所示:
delete [] pvalue; // Delete array pointed to by pvalue
遵循new运算符的类似通用语法,您可以按如下所示分配多维数组-
double** pvalue = NULL; // Pointer initialized with null
pvalue = new double [3][4]; // Allocate memory for a 3x4 array
但是,为多维数组释放内存的语法将仍然与上述相同-
delete [] pvalue; // Delete array pointed to by pvalue
对象与简单数据类型没有什么不同。例如,考虑以下代码,我们将使用对象数组来阐明概念-
考虑一种情况,当我们有两个人在同一个班上使用相同的名字 Zara
。每当我们绝对依赖区分他们时,我们都必须使用一些其他信息以及他们的名字,例如,如果他们居住在不同地区,或者他们的母亲或父亲的名字,等等。
您的C++应用程序中可能会出现相同的情况。例如,您可能正在编写一些具有称为xyz()函数的代码,并且还有另一个库也具有相同的函数xyz()。现在,编译器无法知道您在代码中所引用的xyz()函数的版本。
设计命名空间是为了克服这一困难,它被用作附加信息来区分具有不同库中可用的相同名称的相似函数,类,变量等。使用命名空间,您可以定义用于定义名称的上下文。本质上,命名空间定义了一个作用域。
命名空间定义以关键字命名空间开头,后跟命名空间名称,如下所示:
namespace namespace_name {
// code declarations
}
要调用函数或变量的启用名称空间的版本,请在名称空间名称前添加(::
),如下所示:
name::code; // code could be variable or function.
让我们看看命名空间如何限制包含变量和函数在内的实体-
您还可以避免使用 using namespace
指令在名称空间前添加前缀。该指令告诉编译器,后续代码正在使用指定名称空间中的名称。因此,以下代码隐含了名称空间-
"using
"指令还可以用于引用命名空间中的特定项目。例如,如果您要使用的 std
名称空间的唯一部分是cout
,则可以按以下方式引用它:
using std::cout;
后续代码可以在不添加名称空间的情况下引用 cout
,但是 std
名称空间中的其他项仍依赖如下显式-
using
指令中引入的名称遵循正常的作用域规则。从 using
指令的点到找到该指令的作用域的结尾,该名称都是可见的。隐藏在外部作用域中具有相同名称的实体。
不连续的命名空间 命名空间可以分为几部分定义,因此命名空间由其单独定义的部分之和组成。命名空间的各个部分可以分布在多个文件中。
因此,如果命名空间的一部分依赖在另一个文件中定义的名称,则仍必须声明该名称。 编写以下命名空间定义,或者定义新的命名空间, 或者将新元素添加到现有的命名空间中-
namespace namespace_name {
// code declarations
}
可以嵌套命名空间,您可以在其中定义另一个命名空间中的一个命名空间,如下所示:
namespace namespace_name1 {
// code declarations
namespace namespace_name2 {
// code declarations
}
}
您可以通过使用解析运算符来访问嵌套名称空间的成员,如下所示:
// to access members of namespace_name2
using namespace namespace_name1::namespace_name2;
// to access members of namespace:name1
using namespace namespace_name1;
在上面的语句中,如果您使用 namespace_name1
,则它将使 scope_name2
的元素在范围内可用,如下所示:
模板是通用编程的基础,它涉及以独立于任何特定类型的方式编写代码。
模板是用于创建通用类或函数的蓝图或公式。库容器(如迭代器和算法)是通用编程的示例,并已使用模板概念进行了开发。
每个容器都有一个单独的定义,例如 vector
,但是我们可以定义许多不同种类的向量,例如vector<int>
或vector<string>
。
您可以使用模板来定义函数和类,让我们看看它们是如何工作的-
模板函数定义的一般形式如下所示:
template <class type> ret-type func-name(parameter list) {
// body of function
}
在此,type
是该函数使用的数据类型的占位符名称。该名称可以在函数定义中使用。
以下是返回两个值中最大值的函数模板的示例-
正如我们可以定义函数模板一样,我们也可以定义类模板。通用类声明的一般形式如下所示-
template <class type> class class-name {
.
.
.
}
在这里,type
是占位符类型名称,它将在实例化类时指定。您可以使用逗号分隔的列表来定义多个通用数据类型。
以下是定义类Stack<>
并实现通用方法以从堆栈中推送和弹出元素的示例-
预处理程序是指令,它们向编译器提供指令,以在实际编译开始之前对信息进行预处理。
所有预处理程序指令均以#
开头,并且一行上的预处理程序指令之前只能出现空格字符。预处理程序指令不是C++语句,因此它们不以分号(;
)结尾。
您已经在所有示例中看到了#include
指令。此宏用于将头文件包含到源文件中。
C++支持许多预处理器指令,例如#include
,#define
,#if
,#else
,#line
等。让我们看看重要的指令-
#define
预处理程序指令创建符号常量。符号常量称为宏,
指令的一般形式为-
#define macro-name replacement-text
当该行出现在文件中时,在编译程序之前,该文件中所有随后出现的宏都将由替换文本替换。例如-
#include <iostream>
using namespace std;
#define PI 3.14159
int main () {
cout << "Value of PI :" << PI << endl;
return 0;
}
现在,假设我们拥有源代码文件,让我们对该代码进行预处理以查看结果。因此,让我们使用-E
选项进行编译,并将结果重定向到test.p
。现在,如果您检查test.p,它将有很多信息,在底部,您将找到替换为以下内容的值-
$gcc -E test.cpp > test.p
...
int main () {
cout << "Value of PI :" << 3.14159 << endl;
return 0;
}
您可以使用 #define
定义将采用以下参数的宏-
#include <iostream>
using namespace std;
#define MIN(a,b) (((a)<(b)) ? a : b)
int main () {
int i, j;
i = 100;
j = 30;
cout <<"The minimum is " << MIN(i, j) << endl;
return 0;
}
有几种指令,可用于编译程序源代码的选择性部分。此过程称为条件编译。
条件预处理器构造非常类似于"if
"选择结构。考虑以下预处理器代码-
#ifndef NULL
#define NULL 0
#endif
您可以编译程序以进行调试。您还可以使用单个宏打开或关闭调试,如下所示:
#ifdef DEBUG
cerr <<"Variable x = " << x << endl;
#endif
如果在指令#ifdef DEBUG
之前定义了符号常量DEBUG
,则cerr
语句将在程序中编译。您可以使用#if 0
语句来注释掉程序的一部分,如下所示-
#if 0
code prevented from compiling
#endif
#
和##
预处理运算符在C++
和 ANSI/ISO
C
中可用。#
运算符使替换文本标记转换为带引号的字符串。
考虑以下宏定义-
如果我们编译并运行以上代码,这将产生以下结果-
HELLO C++
让我们看看它是如何工作的。很容易理解C++预处理器改变了思路-
cout << MKSTR(HELLO C++) << endl;
上一行将变成下一行-
cout << "HELLO C++" << endl;
##
运算符用于连接两个标记。这是一个例子-
#define CONCAT( x, y ) x ## y
当 CONCAT
出现在程序中时,其参数将串联起来并用于替换宏。例如,在程序中,将CONCAT(HELLO, C++)
替换为"HELLO C++",如下所示。
如果我们编译并运行以上代码,这将产生以下结果-
100
让我们看看它是如何工作的。很容易理解C++预处理器转换-
cout << concat(x, y);
上一行将转换为下一行-
cout << xy;
宏 | 描述 |
---|---|
__LINE__ |
它包含正在编译的程序的当前行号。 |
__FILE__ |
它包含正在编译的程序的当前文件名。 |
__DATE__ |
它包含格式为month/day/year 的字符串,该字符串是源文件转换为目标代码的日期。 |
__TIME__ |
它包含格式为hour:minute:second 的字符串,该字符串是程序被编译的时间。 |
让我们看一个上面所有宏的例子-
信号是操作系统传递给进程的中断,可以过早地终止程序。您可以通过在 UNIX
,LINUX
,Mac OS X
或 Windows
系统上按Ctrl + C
来生成中断。
有些信号不能被程序捕获,但是下面列出了一些信号,您可以在程序中捕获它们, 并可以根据信号采取适当的措施。这些信号在C++头文件中定义。
信号 | 描述 |
---|---|
SIGABRT | 程序异常终止,例如中止调用。 |
SIGFPE | 错误的算术运算,例如被零除或导致溢出的运算。 |
SIGILL | 检测到非法指令。 |
SIGINT | 程序异常终止,例如中止调用。 |
SIGSEGV | 对存储的无效访问。 |
SIGTERM | 发送到程序的终止请求。 |
C++信号处理库提供函数信号以捕获意外事件。以下是signal()函数的语法-
void (*signal (int sig, void (*func)(int)))(int);
为简单起见,此函数接收两个参数:第一个参数为表示信号编号的整数,第二个参数为指向信号处理函数的指针。
让我们编写一个简单的C++程序,在其中使用signal()函数捕获SIGINT信号。无论您想在程序中捕获什么信号,都必须使用信号函数注册该信号并将其与信号处理程序关联。检查以下示例-
您可以通过raise()
函数生成信号,该函数采用整数信号号作为参数,并具有以下语法。
int raise (signal sig);
此处,sig
是发送任何信号的信号编号:SIGINT
,SIGABRT
,SIGFPE
,SIGILL
,SIGSEGV
,SIGTERM,
SIGHUP`。以下是我们使用raise()函数在内部产生信号的示例,如下所示:
多线程是多任务的一种特殊形式,多任务是一种函数,它使您的计算机可以同时运行两个或多个程序。通常,多任务有两种类型:基于进程和基于线程。
基于进程的多任务处理程序的并发执行。基于线程的多任务处理是同时执行同一程序的各个部分。
多线程程序包含两个或多个可以同时运行的部分。这种程序的每个部分都称为一个线程,并且每个线程都定义了单独的执行路径。
C++不包含对多线程应用程序的任何内置支持。相反,它完全依赖于操作系统来提供此函数。
本教程假定您正在Linux OS上工作,并且我们将使用POSIX
编写多线程C++
程序。 POSIX
线程或Pthread
提供了许多类似Unix的POSIX系统(例如FreeBSD,NetBSD,GNU/Linux,Mac OS X和Solaris)上可用的API。
以下例程用于创建POSIX线程-
#include <pthread.h>
pthread_create (thread, attr, start_routine, arg)
在这里,pthread_create
创建一个新线程并使其可执行。可以在代码中的任何位置多次调用此例程。这是参数的描述-
参数 | 描述 |
---|---|
thread | 子例程返回的新线程的不透明唯一标识符。 |
attr | 一个不透明的属性对象,可用于设置线程属性。您可以指定线程属性对象,或将NULL指定为默认值。 |
start_routine | 创建线程后将执行的C++例程。 |
arg | 可以传递给start_routine的单个参数。它必须通过引用作为void类型的指针转换传递。如果不传递任何参数,则可以使用NULL。 |
进程可以创建的最大线程数取决于实现。一旦创建,线程就是对等的,并且可以创建其他线程。线程之间没有隐含的层次结构或依赖性。
我们使用以下例程终止POSIX线程-
#include <pthread.h>
pthread_exit (status)
在这里,pthread_exit
用于显式退出线程。通常,pthread_exit()
例程在线程完成其工作之后被调用,并且不再依赖存在。
如果main()
在创建线程之前完成,并以pthread_exit()
退出,则其他线程将继续执行。否则,它们将在main()
完成时自动终止。
这个简单的示例代码使用 pthread_create()
例程创建5个线程。每个线程都打印一个"Hello World!"。消息,然后以对 pthread_exit()
的调用终止。
本示例说明如何通过结构传递多个参数。您可以在线程回调中传递任何数据类型,因为它指向 void
,如以下示例中所述-
我们可以使用以下两个例程来连接或分离线程:
pthread_join (threadid, status)
pthread_detach (threadid)
pthread_join()
子例程阻塞调用线程,直到指定的"threadid
"线程终止。创建线程时,其属性之一定义它是可连接的还是可分离的。只有创建为可连接的线程才能被连接。如果线程创建为分离线程,则永远无法加入。
此示例演示如何通过使用Pthread连接例程来等待线程完成。
什么是 CGI
?
-
通用网关接口(
CGI
)是一组标准,用于定义Web服务器和自定义脚本之间如何交换信息。 -
CGI
规范目前由NCSA
维护,NCSA
定义CGI
如下- -
通用网关接口(
CGI
)是外部网关程序与信息服务器(例如HTTP
服务器)接口的标准。
当前版本是CGI/1.1,CGI/1.2正在开发中。
为了理解CGI的概念,让我们看看单击超链接浏览特定网页或URL时会发生什么。
-
您的浏览器与HTTP Web服务器联系,并依赖提供URL。文档名称。
-
Web服务器将解析URL并查找文件名。如果找到请求的文件,则Web服务器将该文件发送回浏览器,否则发送一条错误消息,表明您请求了错误的文件。
-
Web浏览器从Web服务器获取响应,并根据收到的响应显示收到的文件或错误消息。
但是,可以通过以下方式设置HTTP服务器:无论何时请求某个目录中的文件,都不会发回该文件。而是将其作为程序执行,并将程序产生的输出发送回浏览器进行显示。
通用网关接口(CGI
)是一种标准协议,用于使应用程序(称为CGI程序或CGI脚本)能够与Web服务器和客户端进行交互。这些CGI程序可以用 Python
,PERL
,Shell
,C或C++等编写。
在继续进行CGI编程之前,请确保您的Web服务器支持CGI,并且已将其配置为处理CGI程序。 HTTP服务器要执行的所有CGI程序都保存在预先配置的目录中。该目录称为CGI目录,按照约定,其命名为 /var/www/cgi-bin
。按照惯例,CGI
文件的扩展名为.cgi
,尽管它们是C++
可执行文件。
默认情况下,Apache Web Server配置为在 /var/www/cgi-bin
中运行CGI程序。如果要指定任何其他目录来运行CGI脚本,则可以在httpd.conf
文件中修改以下部分-
<Directory "/var/www/cgi-bin">
AllowOverride None
Options ExecCGI
Order allow,deny
Allow from all
</Directory>
<Directory "/var/www/cgi-bin">
Options All
</Directory>
在这里,我假设您已经成功启动并运行了Web Server,并且能够运行任何其他CGI程序,例如Perl或Shell等。
编译以上代码,并将可执行文件命名为cplusplus.cgi
。该文件被保存在 /var/www/cgi-bin
目录中,并且具有以下内容。在运行CGI程序之前,请确保使用chmod 755 cplusplus.cgi
UNIX命令使文件具有更改模式,以使文件可执行。
上面的C++程序是一个简单的程序,它将其输出写入 STDOUT
文件即屏幕上。有一个重要的额外函数可用,即第一行打印Content-type:text/html\r\n\r\n
。该行被发送回浏览器,并指定要在浏览器屏幕上显示的内容类型。现在您必须已经了解了CGI
的基本概念,并且可以使用 Python
编写许多复杂的CGI程序。 C++ CGI程序可以与任何其他外部系统(例如 RDBMS
)进行交互以交换信息。
Content-type:text/html\r\n\r\n
这行是HTTP标头的一部分,发送到浏览器以了解其内容。所有HTTP标头将采用以下形式-
头部字段 | 描述 |
---|---|
Content-type: |
定义要返回的文件格式的MIME字符串。示例是Content-type:text/html。 |
Expires: Date |
信息失效的日期。浏览器应使用它来决定何时依赖刷新页面。有效的日期字符串的格式应为格林尼治标准时间1998年1月1日12:00:00。。 |
Location: URL |
应该返回的URL,而不是请求的URL。您可以使用此字段将请求重定向到任何文件。 |
Last-modified: Date |
资源的最后修改日期 |
Content-length: N |
返回的数据的长度(以字节为单位)。浏览器使用此值报告文件的估计下载时间。 |
Set-Cookie: String |
设置通过字符串传递的cookie。 |
所有 CGI
程序都可以访问以下环境变量。这些变量在编写任何 CGI
程序时都起着重要作用。
变量名 | 描述 |
---|---|
CONTENT_TYPE | 内容的数据类型,在客户端将附件内容发送到服务器时使用。例如文件上传等。 |
CONTENT_LENGTH | 仅可用于 POST 请求的查询信息的长度。 |
HTTP_COOKIE | 以键和值对的形式返回设置的 cookie 。 |
HTTP_USER_AGENT | User-Agent 请求标头字段包含有关发起请求的用户代理的信息。它是 Web 浏览器的名称。 |
PATH_INFO | CGI 脚本的路径。 |
QUERY_STRING | 与 GET 方法请求一起发送的 URL 编码信息。 |
REMOTE_ADDR | 发出请求的远程主机的 IP 地址。这对于记录日志或进行身份验证很有用。 |
REMOTE_HOST | 发出请求的主机的标准名称。如果此信息不可用,则可以使用 REMOTE_ADDR 获取IR地址。 |
REQUEST_METHOD | 发出请求的方法。最常见的方法是 GET 和 POST 。 |
SCRIPT_FILENAME | CGI 脚本的完整路径。 |
SCRIPT_NAME | CGI 脚本的名称。 |
SERVER_NAME | 服务器的主机名或IP地址。 |
SERVER_SOFTWARE | 服务器正在运行的软件的名称和版本。 |
这是一个小的 CGI
程序,列出了所有 CGI
变量。|
对于真实的示例,您将依赖通过 CGI
程序执行许多操作。有一个为C++程序编写的 CGI
库,您可以从ftp://ftp.gnu.org/gnu/cgicc/下载,并按照以下步骤安装该库-
$tar xzf cgicc-X.X.X.tar.gz
$cd cgicc-X.X.X/
$./configure --prefix=/usr
$make
$make install
当您依赖将一些信息从浏览器传递到 Web
服务器以及最终传递给 CGI
程序时,您肯定遇到过许多情况。浏览器最经常使用两种方法将此信息传递到 Web
服务器。这些方法是 GET
方法和 POST
方法。
GET
方法发送附加到页面请求的已编码用户信息。页面和编码信息由?分隔。字符如下-
http://www.test.com/cgi-bin/cpp.cgi?key1=value1&key2=value2
GET
方法是将信息从浏览器传递到Web服务器的默认方法,它会生成一个长字符串,该字符串将出现在浏览器的 Location
:框中。如果您有密码或其他敏感信息要传递到服务器,请不要使用 GET
方法。 GET
方法具有长度限制,您可以在请求字符串中最多传递 1024
个字符。
使用 GET
方法时,将使用 QUERY_STRING
http标头传递信息,并且可以通过QUERY_STRIN
G环境变量在 CGI
程序中访问信息。
您可以通过简单地将键和值对与任何URL串联来传递信息,也可以使用HTML 标记使用GET方法传递信息。
这是一个简单的URL,它将使用GET方法将两个值传递给 hello_get.py
程序。
/cgi-bin/cpp_get.cgi?first_name=ZARA&last_name=ALI
下面是一个程序,用于生成 cpp_get.cgi
CGI
程序来处理 Web
浏览器给出的输入。我们将使用C++ CGI
库,它使访问传递的信息变得非常容易-
C是由 Bell
电话实验室的Dennis M. Ritchie
于 1972
年开发的一种通用的过程式命令式计算机编程语言,用于开发 Unix
操作系统。
C是使用最广泛的计算机语言,它与Java编程语言(在现代软件程序员中同样流行并且使用最广泛的语言)一起保持着排名第一的波动。
C标准库是一组C内置函数,常量和头文件,例如 <stdio.h>
, <stdlib.h>
, <math.h>
,等。该库将用作 C
程序员的参考手册。
C标准库的assert.h
头文件提供了一个称为assert
的宏,该宏可用于验证程序所作的假设并在此假设为 false
时打印诊断消息。
定义的宏断言引用另一个宏 NDEBUG
,它不是<assert.h>
的一部分。
如果将 NDEBUG
定义为源文件中的宏名称,则在包含的位置处,assert
宏的定义如下-
#define assert(ignore) ((void)0)
以下是在头文件assert.h中定义的唯一函数-
void assert(int expression)
这实际上是一个宏,而不是一个函数,可用于在C程序中添加诊断。
C库宏 void assert(int expression) 允许将诊断信息写入标准错误文件。
换句话说,它可用于在C程序中添加诊断。
expression-这可以是变量或任何C表达式。
如果expression的计算结果为 TRUE
,则 assert()
不执行任何操作。
如果表达式的计算结果为FALSE,则assert()在stderr上显示一条错误消息(用于显示错误消息和诊断的标准错误流),并中止程序执行。
此宏不返回任何值。
C标准库的 ctype.h
头文件声明了一些对测试和映射字符有用的函数。
所有函数均接受int作为参数,其值必须为 EOF
或可表示为无符号字符。
如果参数c满足所描述的条件,则所有函数均返回非零(true),否则返回零(false)。
函数 | 描述 |
---|---|
int isalnum(int c) | 此函数检查传递的字符是否为字母数字。 |
int isalpha(int c) | 此函数检查传递的字符是否为字母。 |
int iscntrl(int c) | 此函数检查传递的字符是否为控制字符。 |
int isdigit(int c) | 此函数检查传递的字符是否为十进制数字。 |
int isgraph(int c) | 该函数检查使用区域设置传递的字符是否具有字符表示。 |
int islower(int c) | 此函数检查传递的字符是否为小写字母。 |
int isprint(int c) | 此函数检查传递的字符是否可打印。 |
int ispunct(int c) | 此函数检查传递的字符是否为标点字符。 |
int isspace(int c) | 此函数检查传递的字符是否为空格。 |
int isupper(int c) | 此函数检查传递的字符是否为大写字母。 |
int isxdigit(int c) | 此函数检查传递的字符是否为十六进制数字。 |
C库函数int isspace(char c)检查所传递的字符是否为空格。
标准的空白字符是-
' ' (0x20) space (SPC)
'\t' (0x09) horizontal tab (TAB)
'\n' (0x0a) newline (LF)
'\v' (0x0b) vertical tab (VT)
'\f' (0x0c) feed (FF)
'\r' (0x0d) carriage return (CR)
该库还包含两个转换函数,它们接受并返回一个"int"。
函数 | 描述 |
---|---|
int tolower(int c) | 此函数将大写字母转换为小写字母。 |
int toupper(int c) | 此函数将小写字母转换为大写字母。 |
C库函数int tolower(int c)将给定字母转换为小写。
C标准库的 errno.h
头文件定义了整数变量 errno
,它在发生错误时由系统调用和某些库函数设置,以指示出问题所在。
该宏扩展为 int
类型的可修改左值,因此可以被程序读取和修改。
程序启动时将 errno
设置为零。
标准C库的某些函数会将其值修改为非零值,以表示某些类型的错误。
您还可以方便地修改其值或将其重置为零。
errno.h
头文件还定义了一个指示不同错误代码的宏列表,这些宏将扩展为 int
类型的整数常量表达式。
C库宏 extern int errno
由系统调用和某些库函数设置,以在发生错误时指示是否有任何错误
如上所述,C库宏EDOM表示域错误,如果输入参数在域之外(定义了数学函数并将errno设置为EDOM),则发生域错误。
如上所述,C库宏 ERANGE
表示范围错误,如果输入参数超出范围,则会发生范围错误,在该范围内定义了数学函数并将errno设置为ERANGE。
C标准库的 float.h
头文件包含一组与浮点值相关的各种依赖于平台的常量。
这些常量由ANSI C
提出。它们允许制作更多可移植的程序。
在检查所有常量之前,最好了解浮点数由以下四个元素组成:
组件 | 组件描述 |
---|---|
S | 符号 ( +/- ) |
b | 指数表示形式的基数或基数,二叉表示2,十进制表示10,十六进制表示16,依此类推... |
e | 指数,介于最小emin和最大emax之间的整数。 |
p | 精度,以有效数字为单位的基数b位数。 |
基于以上四个组成部分,浮点的值如下:
floating-point = ( S ) p x be
or
floating-point = (+/-) precision x baseexponent
以下值是特定于实现的,并使用#define
指令定义,但这些值不得低于此处给出的值。
请注意,在所有情况下,FLT
均指代float类型,DBL
指代double型,而LDBL
指代long double型。
宏 | 描述 |
---|---|
FLT_ROUNDS | 定义浮点加法的舍入模式,它可以具有以下任意值:-1 -1-不确定;0-朝向零;1-接近;2-朝向正无穷大;3-朝向负无穷大 |
FLT_RADIX 2 | 这定义了指数的基数基数表示。base-2是二叉,base-10是常规的十进制表示形式,base-16是十六进制。 |
FLT_MANT_DIG DBL_MANT_DIG LDBL_MANT_DIG | 这定义了指数的基数基数表示。base-2是二叉,base-10是常规的十进制表示形式,base-16是十六进制。 |
FLT_DIG 6DBL_DIG 10LDBL_DIG 10 | 这些宏定义了四舍五入后可以无变化表示的最大十进制数字(以10为基) |
FLT_MIN_EXP DBL_MIN_EXP LDBL_MIN_EXP | 这些宏定义基本FLT_RADIX中指数的最小负整数值。 |
FLT_MIN_10_EXP -37 DBL_MIN_10_EXP -37 LDBL_MIN_10_EXP -37 | 这些宏定义以10为底的指数的最小负整数值。 |
FLT_MAX_EXP DBL_MAX_EXP LDBL_MAX_EXP | 这些宏定义基本FLT_RADIX中指数的最大整数值。 |
FLT_MAX_10_EXP +37 DBL_MAX_10_EXP +37 LDBL_MAX_10_EXP +37 | 这些宏定义以10为底的指数的最大整数值。 |
FLT_EPSILON 1E-5 DBL_EPSILON 1E-9 LDBL_EPSILON 1E-9 | 这些宏定义了可表示的最低有效位。 |
FLT_MIN 1E-37 DBL_MIN 1E-37 LDBL_MIN 1E-37 | 这些宏定义最小浮点值。 |
limits.h标头确定各种变量类型的各种属性。
此标头中定义的宏限制了各种变量类型(例如char,int和long)的值。
这些限制指定变量不能存储超出这些限制的任何值,例如,无符号字符最多可以存储255。
以下值是特定于实现的,并使用 #define
指令定义,但这些值不得低于此处给出的值。
宏 | 值 | 描述 |
---|---|---|
CHAR_BIT | 8 | 定义字节中的位数。 |
SCHAR_MIN | -128 | 定义有符号字符的最小值。 |
SCHAR_MAX | +127 | 定义有符号字符的最大值。 |
UCHAR_MAX | 255 | 定义无符号字符的最大值。 |
CHAR_MIN | -128 | 定义char类型的最小值,如果char表示负值,则其值将等于SCHAR_MIN,否则为0。 |
CHAR_MAX | +127 | 定义char类型的值,如果char表示负值,则其值将等于SCHAR_MAX,否则为UCHAR_MAX。 |
MB_LEN_MAX | 16 | 定义多字节字符中的最大字节数。 |
SHRT_MIN | -32768 | 定义short int的最小值。 |
SHRT_MAX | +32767 | 定义short int的最大值。 |
USHRT_MAX | 65535 | 定义无符号short int的最大值。 |
INT_MIN | -2147483648 | 定义整数的最小值。 |
INT_MAX | +2147483647 | 定义int的最大值。 |
UINT_MAX | 4294967295 | 定义无符号int的最大值。 |
LONG_MIN | -9223372036854775808 | 定义长整数的最小值。 |
LONG_MAX | +9223372036854775807 | 定义long int的最大值。 |
ULONG_MAX | 18446744073709551615 | 定义无符号long int的最大值。 |
locale.h
标头定义了位置特定的设置,例如日期格式和货币符号。
您将找到几个定义的宏,以及一个重要的结构struct lconv
和下面列出的两个重要函数。
以下是标头中定义的宏,这些宏将在下面列出的两个函数中使用-
宏 | 描述 |
---|---|
LC_ALL | 设置一切。 |
LC_COLLATE | 影响 strcoll 和 strxfrm 函数。 |
LC_CTYPE | 影响所有字符函数。 |
LC_MONETARY | 影响 localeconv 函数提供的货币信息。 |
LC_NUMERIC | 影响小数点格式和 localeconv 函数提供的信息。 |
LC_TIME | 影响strftime 函数。 |
函数 | 描述 |
---|---|
charsetlocale(int category, const charlocale) | 设置或读取位置相关信息。 |
struct lconv*localeconv(void) | 设置或读取位置相关信息。 |
typedef struct {
char*decimal_point;
char*thousands_sep;
char*grouping;
char*int_curr_symbol;
char*currency_symbol;
char*mon_decimal_point;
char*mon_thousands_sep;
char*mon_grouping;
char*positive_sign;
char*negative_sign;
char int_frac_digits;
char frac_digits;
char p_cs_precedes;
char p_sep_by_space;
char n_cs_precedes;
char n_sep_by_space;
char p_sign_posn;
char n_sign_posn;
} lconv
以下是每个字段的描述-
1 decimal_point
用于非货币值的小数点字符。
2 thousands_sep
用于非货币值的千位分隔符。
3 grouping
(分组)一个字符串,以非货币数量指示每组数字的长度。
每个字符代表一个整数值,该整数值指定当前组中的位数。
值为0表示先前的值将用于其余组。
4 int_curr_symbol
是使用的国际货币符号的字符串。
前三个字符是ISO 4217:1987指定的字符,第四个字符是将货币符号与货币数量分开的字符。
5 currency_symbol
用于货币的本地符号。
6 mon_decimal_point
用于货币值的小数点字符。
7 mon_thousands_sep
用于货币值的千位分组字符。
8 mon_grouping
一个字符串,其元素定义货币值中的数字分组的长度。
每个字符代表一个整数值,该整数值指定当前组中的位数。
值为0表示先前的值将用于其余组。
9 positive_sign
用于正货币值的字符。
10 negative_sign
用于负货币值的字符。
11 int_frac_digits
国际货币值中小数点后显示的位数。
12 frac_digits
以货币值显示的小数点后的位数。
13 p_cs_precedes
如果等于1,则 currency_symbol
出现在正货币值之前。
如果等于0,则 currency_symbol
出现在正的货币值之后。
14 p_sep_by_space
如果等于1,则 currency_symbol
与正货币值之间用空格分隔。
如果等于0,则currency_symbol和正货币值之间没有空格。
15 n_cs_precedes
如果等于1,则 currency_symbol
在负货币值之前。
如果等于0,则 currency_symbol
继承负的货币值。
16 n_sep_by_space
如果等于1,则 currency_symbol
与负货币值之间用空格分隔。
如果等于0,则currency_symbol和负货币值之间没有空格。
17 p_sign_posn
以正的货币值表示正号的位置。
18 n_sign_posn
以负货币值表示 negative_sign
的位置。
Value | Description |
---|---|
0 | 括号封装了值和currency_symbol。. |
1 | 符号在值和currency_symbol之前。 |
2 | 符号在值和currency_symbol之后。 |
3 | 该符号紧跟在值和currency_symbol之前。 |
4 | 该符号立即在value和currency_symbol之后。 |
math.h标头定义了各种数学函数和一个宏。
该库中所有可用的函数都将double作为参数,并返回double作为结果。
此库中仅定义了一个宏-
宏 | 说明 |
---|---|
HUGE_VAL | 当函数的结果可能无法表示为浮点数时,将使用此宏。如果正确结果的长度太大而无法表示,则该函数将errno设置为ERANGE以指示范围误差,并返回由宏HUGE_VAL或其负号(-HUGE_VAL)命名的特定的非常大的值。如果结果的长度太小,则返回零值。在这种情况下,errno可能会或可能不会设置为ERANGE。 |
以下是标头math.h中定义的函数-
1 double acos(double x)返回弧度x的反余弦值。 c_function_acos
2 double asin(double x)以弧度返回x的反正弦值。 c_function_asin
3 double atan(double x)以弧度返回x的反正切。 c_function_atan
4 double atan2(double y,double x)根据两个值的符号返回以y/x弧度表示的反正切,以确定正确的象限。 c_function_atan2
5 double cos(double x)返回弧度角x的余弦。 c_function_cos
6 double cosh(double x)返回x的双曲余弦值。 c_function_cos
7 double sin(double x)返回弧度角x的正弦值。 c_function_sin
8 double sinh(double x)返回x的双曲正弦值。 c_function_sinh
9 double tanh(double x)返回x的双曲正切值。 c_function_tanh
10 double exp(double x)返回e的值乘以x的幂。 c_function_exp
11 double frexp(double x,int* exponent)返回值是尾数,指数所指向的整数是指数。结果值为x =尾数* 2 ^指数。 c_function_frexp
12 double ldexp(double x,int exponent)返回x乘以2的乘幂。 c_function_ldexp
13 double log(double x)返回x的自然对数(以e为底的对数)。 c_function_log
14 double log10(double x)返回x的公共对数(以10为底的对数)。 c_function_log10
15 double modf(double x,double* integer)返回值是小数部分(小数点后的部分),并将整数设置为整数部分。 c_function_modf
16 double pow(double x,double y)返回x乘以y的幂。 c_function_pow
17 double sqrt(double x)返回x的平方根。 c_function_sqrt
18 double ceil(double x)返回大于或等于x的最小整数值。 c_function_sqrt
19 double fabs(double x)返回x的绝对值。 c_function_fabs
20 double floor(double x)返回小于或等于x的最大整数值。 c_function_floor
21 double fmod(double x,double y)返回x除以y的余数。 c_function_fmod
setjmp.h标头定义了宏setjmp(),一个函数longjmp()和一个变量类型jmp_buf,用于绕过常规函数调用和返回规则。
signal.h标头定义了一个变量类型sig_atomic_t,两个函数调用以及几个宏,以处理程序执行期间报告的不同信号。
变量 | 描述 |
---|---|
sig_atomic_t |
这是int类型,用作信号处理程序中的变量。这是对象的一种整体类型,即使存在异步信号也可以作为原子实体进行访问。 |
以下是标头signal.h中定义的宏,这些宏将在下面列出的两个函数中使用。
SIG_ 宏与信号函数一起使用以定义信号函数。
宏 | 描述 |
---|---|
SIG_DFL | 默认信号处理程序。 |
SIG_ERR | 表示错误信号。 |
SIG_IGN | 忽略信号。 |
SIG宏用于在以下情况下表示信号编号:
宏 | 描述 |
---|---|
SIGABRT | 程序异常终止。 |
SIGFPE | 浮点错误,例如被零除。 |
SIGILL | 非法操作。 |
SIGINT | 中断信号,例如ctrl-C。 |
SIGSEGV | 对存储的无效访问(例如段违规)。 |
SIGTERM | 终止请求。 |
以下是标头信号中定义的 signal.h-
函数 | 描述 |
---|---|
void (*signal(int sig, void (*func)(int)))(int) | 该函数设置用于处理信号的函数,即信号处理程序。 |
int raise(int sig) | 此函数导致生成信号sig。sig参数与SIG宏兼容。 |
c_function_signal c_function_raise
stdarg.h标头定义了一个变量类型va_list和三个宏,当未知数量的参数(即可变数量的参数)时,可用于获取函数中的参数。
变量参数的函数在参数列表的末尾用省略号(,...)定义。
以下是标头 stdarg.h 中定义的变量类型-
变量 | 描述 |
---|---|
va_list | 这种类型适合于保存三个宏va_start(),va_arg()和va_end()依赖的信息。 |
以下是标头stdarg.h中定义的宏-
宏 | 描述 |
---|---|
void va_start(va_list ap, last_arg) | 该宏初始化ap变量,以与va_arg和va_end宏一起使用。last_arg是传递给函数的最后一个已知固定参数,即省略号之前的参数。 |
type va_arg(va_list ap, type) | 此宏检索类型为type的函数的参数列表中的下一个参数。 |
void va_end(va_list ap) | 此宏允许使用带有可变参数的函数,该函数使用va_start宏返回。如果从函数返回之前未调用va_end,则结果不确定。 |
C库宏void va_start(va_list ap,last_arg)
初始化ap
变量,以便与va_arg
和va_end
宏一起使用。
last_arg
是传递给函数的最后一个已知固定参数,即省略号之前的参数。
在使用va_arg和va_end之前,必须先调用此宏。
C库宏类型va_arg(va_list ap,type)检索类型为函数的参数列表中的下一个参数。
这不能确定检索到的参数是否是传递给函数的最后一个参数。
C库宏void va_end(va_list ap)允许具有可变参数的函数使用va_start宏返回。
如果从函数返回之前未调用va_end,则结果不确定
stddef.h标头定义了各种变量类型和宏。
其中许多定义也出现在其他标题中。
以下是标头 stddef.h
中定义的变量类型-
变量 | 描述 |
---|---|
ptrdiff_t | 这是有符号整数类型,是两个指针相减的结果。 |
size_t | 这是无符号整数类型,是sizeof关键字的结果。 |
wchar_t | 这是宽字符常量长度的整数类型。 |
以下是标头stddef.h中定义的宏-
宏 | 描述 |
---|---|
NULL | 此宏是空指针常量的值。 |
offsetof(type, member-designator) | 这将导致一个恒定长度为 size_t 的整数,该整数是结构成员从结构开头开始的偏移量(以字节为单位)。成员由成员标识符指定,结构的名称由类型指定。 |
NULL 是空指针常量的值。
根据编译器供应商的不同,它可以定义为((void*)0),0或0L。
C库宏offsetof(type,member-designator)宏产生一个size_t类型的常量整数,它是结构成员从结构开始处的字节偏移量。
成员由成员标识符指定,结构的名称由类型指定。
stdio.h标头定义了三种变量类型,几个宏以及用于执行输入和输出的各种函数。
库变量以下是在头stdio.h中定义的变量类型-
变量 | 描述 |
---|---|
size_t | 这是无符号整数类型,是sizeof关键字的结果。 |
FILE | 这是一种适合于存储文件流信息的对象类型。 |
fpos_t | 这是一种适用于在文件中存储任何位置的对象类型。 |
以下是标头stdio.h中定义的宏-
1 NULL
此宏是空指针常量的值。
2 _IOFBF
,_IOLBF
和_IONBF
这些宏扩展为具有不同值的整数常量表达式,适合用作setvbuf函数的第三个参数。
3 BUFSIZ
该宏是整数,代表setbuf函数使用的缓冲区的长度。
4 EOF
该宏是一个负整数,表示已到达文件末尾。
5 FOPEN_MAX
该宏是整数,代表系统可以保证同时打开的最大文件数。
6 FILENAME_MAX
该宏是一个整数,表示适合于保留可能的最长文件名的char数组的最长长度。
如果实施不施加限制,则该值应为建议的最大值。
7 L_tmpnam
该宏是一个整数,表示适合于保存由tmpnam函数创建的可能的最长临时文件名的char数组的最长长度。
8 SEEK_CUR
,SEEK_END
和 SEEK_SET
这些宏在fseek函数中用于定位文件中的不同位置。
9 TMP_MAX
此宏是tmpnam函数可以生成的唯一文件名的最大数量。
10 stderr
,stdin
和 stdout
这些宏是指向与标准错误,标准输入和标准输出流相对应的 FILE
类型的指针。
以下是头文件stdio.h中定义的函数-
1 int fclose(FILE* stream)关闭流。 所有缓冲区均被刷新。
2 void clearerr(FILE* stream)清除给定流的文件结尾和错误指示符。
3 int feof(FILE* stream)测试给定流的文件结束指示符。
4 int ferror(FILE* stream)测试给定流的错误指示符。
5 int fflush(FILE* stream)刷新流的输出缓冲区。
6 int fgetpos(FILE* stream,fpos_t* pos)获取流的当前文件位置并将其写入pos。
7 FILE* fopen(const char* filename,const char* mode)使用给定模式打开filename指向的文件名。
8 size_t fread(void* ptr,size_t size,size_t nmemb,FILE* stream)将数据从给定流读取到ptr指向的数组中。
9 FILE* freopen(const char文件名,const char mode,FILE* stream)将新文件名与给定的打开流关联,同时关闭流中的旧文件。
10 int fseek(FILE* stream,long int offset,int whence)将流的文件位置设置为给定的偏移量。
参数offset表示要从给定的位置搜索的字节数。
11 int fsetpos(FILE* stream,const fpos_t* pos)将给定流的文件位置设置为给定位置。
参数pos是函数fgetpos给出的位置。
12 long int ftell(FILE* stream)返回给定流的当前文件位置。
13 size_t fwrite(const void* ptr,size_t size,size_t nmemb,FILE* stream)将数据从ptr指向的数组写入给定流。
14 int remove(const char* filename)删除给定的文件名,使其不再可访问。
15 int rename(const char* old_filename,const char* new_filename)使由old_filename引用的文件名更改为new_filename。
16 void rewind(FILE* stream)将文件位置设置为给定流的文件的开头。
17 void setbuf(FILE* stream,char* buffer)定义应如何缓冲流。
18 int setvbuf(FILE* stream,char* buffer,int模式,size_t长度)另一个函数,用于定义应如何缓冲流。
19 FILE* tmpfile(void)以二叉更新模式(wb +)创建一个临时文件。
20 char* tmpnam(char* str)生成并返回一个不存在的有效临时文件名。
21 int fprintf(FILE* stream,const char* format,...)将格式化的输出发送到流。
22 int printf(const char* format,...)将格式化的输出发送到stdout。
23 int sprintf(char* str,const char* format,...)将格式化的输出发送到字符串。
24 int vfprintf(FILE* stream,const char* format,va_list arg)使用参数列表将格式化的输出发送到流。
25 int vprintf(const char* format,va_list arg)使用参数列表将格式化的输出发送到stdout。
26 int vsprintf(char* str,const char* format,va_list arg)使用参数列表将格式化的输出发送到字符串。
27 int fscanf(FILE* stream,const char* format,...)从流中读取格式化的输入。
28 int scanf(const char* format,...)从stdin读取格式化的输入。
29 int sscanf(const char* str,const char* format,...)从字符串读取格式化的输入。
30 int fgetc(FILE* stream)从指定的流中获取下一个字符(无符号字符),并使该流的位置指示符前进。
31 char* fgets(char* str,int n,FILE* stream)从指定的流中读取一行并将其存储到str指向的字符串中。
当读取(n-1)个字符,读取换行符或到达文件末尾(以先到者为准)时,它将停止。
32 int fputc(int char,FILE* stream)将参数char指定的字符(无符号字符)写入指定的流,并使该流的位置指示符前进。
33 int fputs(const char* str,FILE* stream)将字符串写入指定的流,直到但不包含空字符。
34 int getc(FILE* stream)从指定的流中获取下一个字符(无符号字符),并使该流的位置指示符前进。
35 int getchar(void)从标准输入中获取一个字符(无符号字符)。
36 char* gets(char* str)从stdin读取一行并将其存储到str指向的字符串中。
当读取换行符或到达文件结尾时(以先到者为准),它将停止。
37 int putc(int char,FILE* stream)将参数char指定的字符(无符号char)写入指定的流,并使该流的位置指示符前进。
38 int putchar(int char)将参数char指定的字符(无符号char)写入stdout。
39 int puts(const char* str)将一个字符串写入stdout,直到但不包含空字符。
换行符附加到输出。
40 int ungetc(int char,FILE* stream)将字符char(无符号字符)压入指定的流,以便读取下一个字符。
41 void perror(const char* str)将描述性错误消息打印到stderr。
首先打印字符串 str
,然后打印冒号和空格。
stdlib.h标头定义了四个变量类型,几个宏和用于执行常规函数的各种函数。
变量 | 描述 |
---|---|
size_t | 这是无符号整数类型,是sizeof关键字的结果。 |
wchar_t | 这是一个宽字符常量长度的整数类型。 |
div_t | 这是一个宽字符常量长度的整数类型。 |
ldiv_t | 这是ldiv函数返回的结构。 |
库宏以下是标头stdlib.h中定义的宏-
1 NULL
此宏是空指针常量的值。
2 EXIT_FAILURE
这是在失败时要返回的退出函数的值。
3 EXIT_SUCCESS
这是在成功的情况下返回函数的值。
4 RAND_MAX
该宏是rand函数返回的最大值。
5 MB_CUR_MAX
此宏是多字节字符集中最大字节数,不能大于 MB_LEN_MAX
。
以下是标头stdlib.h中定义的函数-
1 double atof(const char* str)将参数str指向的字符串转换为浮点数(double类型)。 c_function_atof
2 int atoi(const char* str)将参数str指向的字符串转换为整数(int类型)。
3 long int atol(const char* str)将参数str指向的字符串转换为long整数(类型long int)。
4 double strtod(const char* str,char** endptr)将参数str指向的字符串转换为浮点数(类型为double)。
5 long int strtol(const char* str,char** endptr,int base)将参数str指向的字符串转换为长整数(类型为long int)。
6 unsigned long int strtoul(const char* str,char** endptr,int base)将参数str指向的字符串转换为unsigned long整数(类型为unsigned long int)。
7 void* calloc(size_t nitems,size_t size)分配所请求的内存并返回指向它的指针。
8 void free(void* ptr取消分配先前由对calloc,malloc或realloc的调用分配的内存。9 void* malloc(size_t size)分配所请求的内存并返回指向它的指针。10 void* realloc(void* ptr
int atexit(void(* func)(void),size_t size)尝试调整ptr指向的内存块的长度,该内存块先前是通过调用malloc或calloc分配的。11 void abort(void)导致程序异常终止。
)导致程序正常终止时调用指定的函数func 13 void exit(int status)导致程序正常终止14 char* getenv(const char* name)搜索由name指向的环境字符串并返回
15 int system(const char* string)字符串指定的命令传递到主机环境,由命令处理器执行
16 void* bsearch(const void* key,const void* base, size_t个下标,size_t长度,int(* compar)(const void*,const void*))执行二分搜索。
17 void qsort(void* base,size_t nitems,size_t size,int(* compar)(const void*,const void*))对数组进行排序。
18 int abs(int x)返回x的绝对值。
19 div_t div(int numer,int denom)用分子(分母)除以分子(分子)。
20 long int labs(long int x)返回x的绝对值。
21 ldiv_t ldiv(long int numer,long int denom)用分子(分母)除以分子(分母)。
22 int rand(void)返回一个伪随机数,范围为0到RAND_MAX。
23 void srand(无符号int种子)此函数为rand函数使用的随机数生成器播种。
24 int mblen(const char* str,size_t n)返回参数str指向的多字节字符的长度。
25 size_t mbstowcs(schar_t* pwcs,const char* str,size_t n)将参数str指向的多字节字符字符串转换为pwcs指向的数组。
26 int mbtowc(whcar_t* pwc,const char* str,size_t n)检查参数str指向的多字节字符。
27 size_t wcstombs(char* str,const wchar_t* pwcs,size_t n)将存储在数组pwcs中的代码转换为多字节字符,并将其存储在字符串str中。
28 int wctomb(char* str,wchar_t wchar)检查与参数wchar给定的多字节字符对应的代码。
以下仅介绍比较重要的标准库
函数对象是专门设计用于与函数语法相似的语法的对象。
std::function的实例可以存储,复制和调用任何Callable目标-函数,lambda表达式,绑定表达式或其他函数对象,以及指向成员函数的指针和指向数据成员的指针。
以下是std::function的声明。
template<class >
class function;
C++ 11
template< class R, class... Args >
class function<R(Args...)>
- R 返回的结果
- argument_type 如果sizeof ...(Args)== 1并且T是Args中的第一个也是唯一的类型,则为T。
成员函数 | 定义 |
---|---|
(constructor) | 它用于构造一个新的std::function实例 |
(destructor) | 它用于销毁std::function实例 |
operator= | 用于分配新目标 |
swap | 用于交换内容 |
assign | 用于分配新目标 |
operator | bool 用于检查是否包含有效目标 |
operator() | 用于调用目标 |
成员函数 | 定义 |
---|---|
std::swap |
它专门研究std::swap 算法2 |
operator== operator!= |
它将std::function 与nullptr 进行比较 |
1 bit_and
是按位 AND
函数对象类别
2 bit_or
是按位 OR
函数对象类别
3 bit_xor
是按位 XOR
函数对象类别3划分是是划分函数对象类别
4 equal_to
是用于相等比较的函数对象类别
5 greater
是大于等于比较的函数对象类
6 greater_equal
是大于等于比较的函数对象类
7 less
是小于等于比较的函数对象类
8 less_equal
是用于小于或等于比较的函数对象类
9 logical_and
是逻辑 AND
函数对象类
10 logic_not
是逻辑 NOT
函数对象类
11 logical_or
是逻辑 OR
函数对象类
12 minus
是减法函数对象类别
13 modulus
是模函数对象类别
14 multiplies
以是乘法函数对象类别
15 negate
是负函数对象类别
16 not_equal_to
是 一个用于非相等比较的函数对象类
17 plus
它是一个加法函数对象类
它构造一个将 args
传递给其构造函数的T
类型的对象,
并返回一个拥有并存储指向它的指针的shared_ptr
类型的对象。
它使用 alloc
为T
类型的对象分配内存,并通过将args
传递给其构造函数进行构造。
该函数返回一个shared_ptr
类型的对象,该对象拥有并存储指向该构造对象的指针。
它返回正确类型的sp
的副本,其存储的指针从U*
静态转换为T*
。
它返回正确类型的sp的副本,其存储的指针从U动态转换为T。
它返回正确类型的 sp
的副本,并将其存储的指针const
从U*
强制转换为T*
。
它返回一个指向sp拥有的删除器的指针。
它定义了函数对象,这些函数对象在shared_ptr和/或weak_ptr对象之间执行基于所有者的比较。
它在派生类中启用了shared_from_this成员函数。
它是一个标头,描述了用于管理C++中的动态存储的函数。标头还定义了一些特定类型和set_new_handler/get_new_handler函数对,如下所示-
函数和说明
1个运算符new分配存储空间。
它分配长度字节的存储空间,适当对齐以表示该长度的任何对象, 并返回指向该块第一个字节的非空指针。
2运算符new []它为数组分配存储空间。
3操作符删除释放存储空间。
4运算符delete []释放数组的存储空间。
5 get_new_handler用于获取新的处理函数。
Sr.No.
类型和描述
1 nothrow_t是一个nathrow类型。
2 new_handler它是一种新的处理函数。
3 bad_alloc这是一个异常,并引发分配内存失败。 cpp_new_bad_alloc
4 bad_array_new_length这是一个错误的数组长度异常。 bad_array_new_length
常量和说明
1 nothrow这是一个nathrow常量。
数组是固定长度的序列容器。
容器是保存相同类型数据的对象。
顺序容器严格按线性顺序存储元素。
容器类使用隐式构造函数静态地分配依赖的内存。
内存是在编译时分配的,因此数组长度无法在运行时缩小或扩展。
数组中的所有元素都位于连续的内存位置。
- | 成员类型 | 定义 |
---|---|---|
1 | value_type | T (模板的第一个参数) |
2 | reference | value_type& |
3 | const_reference | const value_type& |
4 | pointer | value_type* |
5 | const_pointer | const value_type* |
6 | iterator | 一个对 value_type 的随机访问迭代器 |
7 | const_iterator | const value_type 的随机访问迭代器 |
8 | reverse_iterator | std::reverse_iterator |
9 | const_reverse_iterator | std::reverse_iterator <const_iterator> |
10 | size_type | size_t |
11 | difference_type | ptrdiff_t |
Array
中的函数下面是头中所有方法的列表。
1 | array::at | 返回对给定数组容器中位置N处存在的元素的引用。 |
2 | array::back | 返回对数组容器最后一个元素的引用。 |
3 | array::begin | 返回一个迭代器,该迭代器指向数组的开始。 |
4 | array::cbegin | 返回一个常量迭代器,它指向数组的开始。 |
5 | array::cend | 返回一个常量迭代器,它指向array的过去元素。 |
6 | array::crbegin | 返回一个常量反向迭代器,指向数组的最后一个元素。 |
7 | array::crend | 返回一个常量反向迭代器,该迭代器指向末尾。 |
8 | array::data | 返回一个指向数组容器第一个元素的指针。 |
9 | array::empty | 测试数组的长度是否为零。 |
10 | array::end | 返回一个迭代器,该迭代器指向array的past-end元素。 |
11 | array::fill | 为数组的所有元素设置给定值。 |
12 | array::front | 返回对数组容器的第一个元素的引用。 |
13 | array::max_size | 返回数组容器可以容纳的最大元素数。 |
14 | array::operator [] | 返回对给定数组容器中位置N处存在的元素的引用。 |
15 | array::rbegin | 返回指向数组最后一个元素的反向迭代器。 |
16 | array::rend | 返回一个反向迭代器,它指向数组第一个元素之前的理论元素。 |
17 | array::size | 返回数组中存在的元素数。 |
18 | array::swap | 交换两个数组的内容。 |
array::at cpp_array_ataaa
array::back cpp_array_back
array::begin cpp_array_begin
array::begin cpp_array_cbegin
array:cend cpp_array_cend
array::swap cpp_array_swap
非成员重载函数
1 get(array)
返回对数组容器的第I个元素的引用。
2 bool operator==
测试两个容器是否相同3 = bool运算符!=测试两个容器是否相同4 bool运算符<测试第一个数组容器是否小于第二个。
5 bool operator<=
测试第一个数组容器是否小于或等于第二个。
6 bool operator>
测试第一个数组容器是否大于第二个。
7 bool operator>=
测试第一个数组容器是否大于或等于第二个。
位集表示固定长度的N位序列,并存储0或1的值。0表示值为false或未设置位,而1表示值为true或设置了位。
Bitset
类模拟布尔值的空间高效数组,其中每个元素仅占用一位。
当它模拟数组时,其索引也从第0个位置开始。
可以使用下标运算符访问位集中的单个位。
例如,要访问位集 foo
的第一个元素,请使用foo[0]。
Bitset类为构造函数提供了从整数以及从字符串创建位集的函数。
位集的长度在编译时是固定的。
STL
提供了 vector
类,该类提供了动态调整长度函数。
Deque是Double Ended Queue的首字母缩写。
它是一个序列容器,可以更改其运行时长度。
容器是保存相同类型数据的对象。
顺序容器严格按线性顺序存储元素。
双端队列的元素可以分散在不同的内存块中。
容器存储必要的信息,以允许在恒定时间内直接访问任何元素。
与向量不同,双端队列不能保证将其所有元素存储在连续的内存位置。
因此,它不允许通过偏移指针直接访问数据。
但是,它允许使用下标运算符[]
直接访问任何元素。
在运行时,双端队列可以根据依赖从两端收缩或扩展。
内部分配器自动满足存储依赖。
Deque
提供与矢量类似的函数,但提供了从任何一端插入和删除数据的有效方法。
长度为零的双端队列也有效。
在这种情况下,deque.begin()
和deque.end()
指向相同的位置。
但是调用front()或back()的行为是不确定的。
1 deque::assign 通过替换旧的值,为deque元素分配新值。
2 deque::assign 填充版本通过替换旧的为deque元素分配新值。
3 deque::assign 初始值设定项列表版本通过替换旧的为deque元素分配新值。
4 deque::at 返回对在双端队列中位置n处存在的元素的引用。
5 deque::back返回对双端队列的最后一个元素的引用。
6 deque::begin返回一个随机访问迭代器,指向该双端队列的第一个元素。
7 deque::cbegin返回一个常数随机访问迭代器,该迭代器指向双端队列的开始。
8 deque::cend返回一个常数随机访问迭代器,该迭代器指向双端队列的开始。
9 deque::clear通过从双端队列中删除所有元素并将双端队列的长度设置为零来破坏双端队列。
10 deque::crbegin返回一个常量反向迭代器,该迭代器指向容器的反向器开头。
11 deque::crend返回一个常数反向迭代器,该迭代器指向双端队列的反向端点。
12 deque::emplace通过在位置插入新元素来扩展容器。
13 deque::emplace_back在双端队列的末尾插入新元素。
14 deque::emplace_front在双端队列的开头插入新元素。
15 deque::empty测试双端队列是否为空。
16 deque::end返回一个迭代器,该迭代器指向deque容器中的past-the-end元素。
17 deque::erase position version从双端队列中删除单个元素。
18 deque::erase range version从双端队列中删除单个元素。
19 deque::front返回对双端队列的第一个元素的引用20 deque::get_allocator返回与双端队列相关的分配器21 deque ::插入单个元素版本通过在位置插入新元素来扩展容器。
22 deque::insert填充版本通过在容器中插入新元素来扩展容器。
23 deque::insert range version通过在容器中插入新元素来扩展容器。
24 deque::insert move版本通过在容器中插入新元素来扩展容器。
25 deque::insert初始化器列表版本通过在容器中插入新元素来扩展容器。
26 deque::max_size返回双端队列可以容纳的最大元素数。
27 deque::operator =复制版本通过替换旧内容将新内容分配给双端队列,并在必要时修改长度。
28 deque::operator = move version通过替换旧内容将新内容分配给双端队列,并在必要时修改长度。
29 deque::operator =初始值设定项列表版本通过替换旧内容将新内容分配给双端队列,并在必要时修改长度。
30 deque::operator []返回对位置n处存在的元素的引用。
31 deque::pop_back从双端队列中删除最后一个元素,并将双端队列的长度减小一个。
32 deque::pop_front从双端队列中删除第一个元素,并将双端队列的长度减小一个。
33 deque::push_back在双端队列的末尾插入新元素,并将双端队列的长度增加1。
34 deque::push_back移动版本在双端队列的末尾插入新元素,并将双端队列的长度增加1。
35 deque::push_front在deque的前面插入新元素,并将deque的长度增加1。
36 deque::push_front移动版本在deque的前面插入新元素,并将deque的长度增加1。
37 deque::rbegin返回一个反向迭代器,它指向双端队列的最后一个元素。
38 deque::rend返回一个反向迭代器,该迭代器指向双端队列的反向端点。
39 deque::resize更改双端队列的长度。
40 deque::resize值版本更改双端队列的长度。
41 deque::shrink_to_fit请求容器减小其容量以适合其长度。
42 deque::size返回双端队列中存在的元素数。
43 deque::swap用另一个deque x的内容交换deque的内容。
forward_list 是一种常用的序列容器。
容器是保存相同类型数据的对象。
forward_list 容器被实现为单链表,因此它提供了对其数据的单向顺序访问。
forward_list 不提供快速随机访问,它仅支持一个方向的顺序访问。
forward_list 允许在恒定时间内在序列中的任何位置进行插入和删除操作。
forward_lis t的元素可以分散在不同的内存块中。
容器存储必要的信息,以允许顺序访问其数据。
Forward_lists 可以在运行时从两端根据依赖缩小或扩展。
内部分配器自动满足存储依赖。
零长度的forward_lists也有效。
在这种情况下,forward_list.begin()和forward_list.end()指向同一位置。
但是调用front()的行为是不确定的。
List是一种常用的序列容器。
容器是保存相同类型数据的对象。
列表容器实现为双链表,因此它提供了对其数据的双向顺序访问。
列表不提供快速随机访问,它仅支持双向顺序访问。
List 允许在恒定时间内在序列中的任何位置进行插入和删除操作。
列表的元素可以分散在不同的内存块中。
容器存储必要的信息,以允许顺序访问其数据。
列表可以在运行时从两端根据依赖缩小或扩展。
内部分配器自动满足存储依赖。
长度为零的列表也有效。
在这种情况下,list.begin()和list.end()指向同一位置。
但是调用front()或back()的行为是不确定的。
Map是类词典的数据结构。
它是(键,值)对的序列,其中每个唯一键仅与单个值相关联。
它通常被称为关联数组。
在Map中,键值通常用于对元素进行排序。
对于Map数据,键和值的类型可以不同,它表示为:
typedef pair<const Key, T> value_type;
映射通常实现为二叉搜索树。
零长度的映射也是有效的。
在这种情况下,map.begin()和map.end()指向同一位置。
队列是一种数据结构,旨在在FIFO(先进先出)上下文中运行。
在队列中,元素从后端插入,并从前端删除。
队列类是容器适配器。
容器是保存相同类型数据的对象。
可以从不同的序列容器创建队列。
容器适配器不支持迭代器,因此我们不能将其用于数据操作。
但是,它们分别支持push()和pop()成员函数用于数据插入和删除。
集合是一个关联容器,其中包含一组排序的键类型的唯一对象。
每个元素只能出现一次,因此不允许重复。
关联容器有四种:set,multiset,map和multimap。
集合中元素的值不能在容器中修改一次,即元素始终为 const
。
但是可以将它们插入容器或从容器中取出。
集合容器通过键访问单个元素的速度通常比 unordered_set
容器慢,但是它们允许基于子集的顺序直接迭代子集。
无序映射是像字典一样的数据结构。
它是(键,值)对的序列,其中每个唯一键仅与单个值相关联。
它通常被称为关联数组。
它使您能够根据其键快速检索各个元素。
它还实现了直接访问运算符(subscript operator []),该运算符允许使用键值作为参数直接访问映射值。
无序映射不会针对其键或映射值以任何特定顺序对元素进行排序,而是根据其哈希值组织到存储桶中,以允许直接通过其键值快速访问各个元素。
当通过键访问单个元素时,无序映射的性能比映射更好。
但是对于范围迭代,它们的性能相当低。
它是一个关联容器,不按特定顺序存储唯一元素,并允许根据其值快速检索单个元素。
向量是可以更改长度的序列容器。
容器是保存相同类型数据的对象。
顺序容器严格按线性顺序存储元素。
Vector将元素存储在连续的内存位置,并允许使用下标运算符[]直接访问任何元素。
与数组不同,向量可以在运行时根据依赖缩小或扩展。
向量的存储将自动处理。
为了在运行时支持收缩和扩展函数,矢量容器可以分配一些额外的存储空间以适应可能的增长,因此容器的实际容量大于该长度。
因此,与 array
相比,向量消耗更多内存以换取以有效方式管理存储和动态增长的能力。
零长度的向量也是有效的。
在这种情况下,vector.begin()和vector.end()指向相同的位置。
但是调用front()或back()的行为是不确定的。
它是一个类似指针的对象,可以使用++进行递增,使用*取消引用,然后使用!=与另一个迭代器进行比较。
算法库提供了几种可用于多种目的的函数,例如搜索,排序,计数,操纵等。
这些函数作用于元素的范围,并且范围定义为[first,last)
。
1 algorithm::adjacent_find()查找两个连续的相同元素的首次出现,如果相同元素连续存在,则返回指向第一个元素的迭代器,否则返回指向最后一个元素的迭代器。
2 algorithm::adjacent_find()查找两个连续的相同元素的首次出现,如果相同元素连续存在,则返回指向第一个元素的迭代器,否则返回指向最后一个元素的迭代器。
3 algorithm::all_of()如果谓词对于first到last范围内的所有元素都返回true,则返回true。
4 algorithm::any_of()如果谓词对于first到last范围内的任何元素返回true,则返回true。
5 algorithm::binary_search()测试值是否按排序顺序存在。
6 algorithm::binary_search()测试值是否按排序顺序存在。
7 algorithm::copy()将一系列元素复制到新位置。
8 algorithm::copy_backward()以反向顺序将一系列元素复制到新位置。 9 algorithm::copy_if()如果谓词对值返回true,则将一系列元素复制到新位置。
10 algorithm::copy_n()将前n个数字复制到新位置。
11 algorithm::count()返回范围内value出现的次数。
12 algorithm::count_if()返回满足条件的范围中值出现的次数。
13 algorithm::equal()测试两组元素是否相等。
14 algorithm::equal()测试两组元素是否相等。
15 algorithm::equal_range()返回与特定键匹配的元素范围。
16 algorithm::equal_range()返回与特定键匹配的元素范围。
17 algorithm::fill()将某些值分配给一系列元素。
18 algorithm::fill_n()将值赋给first指向的序列的前n个元素。
19 algorithm::fill_n()将值赋给first所指向序列的前n个元素。
20 algorithm::find()查找元素的第一个匹配项。
21 algorithm::find_end()查找元素的最后一次出现。
22 algorithm::find_end()查找元素的最后一次出现。
23 algorithm::find_first_of()返回与(first1,last1)范围内匹配first2,last2中任何元素的第一个元素的迭代器。
24 algorithm::find_first_of()返回与(first1,last1)范围内匹配first2,last2中任何元素的第一个元素的迭代器。
25 algorithm::find_if()查找满足条件的元素的首次出现。
26 algorithm::find_if_not()查找满足条件的元素的最后一次出现。
27 algorithm::for_each()将提供的函数应用于范围的每个元素。
28 algorithm::generate()将gen的连续调用返回的值分配给first至last范围内的元素。
29 algorithm::generate_n()将连续调用gen返回的值分配给第一个指向的序列的前n个元素。
30 algorithm::generate_n()将连续调用gen返回的值分配给第一个指向的序列的前n个元素。
31 algorithm::includes()测试第一个集合是否是另一个的子集。
32 algorithm::includes()测试第一个集合是否是另一个的子集。
33 algorithm::inplace_merge()合并两个已排序的序列。
34 algorithm::inplace_merge()合并两个已排序的序列。
35 algorithm::is_heap()测试给定的序列是否为最大堆。
36 algorithm::is_heap()测试给定的序列是否为最大堆。
37 algorithm::is_heap_until()从序列中找到违反最大堆条件的第一个元素。
38 algorithm::is_heap_until()从序列中找到违反最大堆条件的第一个元素。
39 algorithm::is_partitioned()测试范围是否被分区。
40 algorithm::is_permutation()测试一个序列是否是其他序列的置换。
41 algorithm::is_permutation()测试一个序列是否是其他序列的置换。
42 algorithm::is_sorted()测试范围是否被排序。
43 algorithm::is_sorted()测试范围是否被排序。
44 algorithm::is_sorted_until()从序列中查找第一个未排序的元素。
45 algorithm::is_sorted_until()从序列中查找第一个未排序的元素。
46 algorithm::iter_swap()交换两个迭代器指向的对象的值。
47 algorithm::lexicographical_compare()测试一个范围在字典上是否小于另一个范围。
48 algorithm::lexicographical_compare()测试一个范围在字典上是否小于另一个范围。
49 algorithm::lower_bound()查找不小于给定值的第一个元素。
50 algorithm::lower_bound()查找不小于给定值的第一个元素。