C++ 快速指南
C++概要 (C++ Overview)
C ++是一种静态类型,编译,通用,区分大小写,自由格式的编程语言,支持过程,面向对象和通用编程。
C ++被认为是一种middle-level语言,因为它包含高级和低级语言特性的组合。
C ++由Bjarne Stroustrup于1979年在新泽西州Murray Hill的贝尔实验室开发,作为C语言的增强版,最初命名为C with Classes,但后来在1983年更名为C ++。
C ++是C的超集,几乎任何合法的C程序都是合法的C ++程序。
Note - 在编译时执行类型检查而不是运行时,编程语言被称为使用静态类型。
Object-Oriented Programming
C ++完全支持面向对象的编程,包括面向对象开发的四大支柱 -
- Encapsulation
- 数据隐藏
- Inheritance
- Polymorphism
标准库
标准C ++包含三个重要部分 -
核心语言提供所有构建块,包括变量,数据类型和文字等。
C ++标准库提供了丰富的函数集,用于处理文件,字符串等。
标准模板库(STL)提供了一组操作数据结构等的丰富方法。
ANSI标准
ANSI标准试图确保C ++是可移植的; 您为Microsoft编译器编写的代码将使用Mac,UNIX,Windows框或Alpha上的编译器进行编译而不会出错。
ANSI标准已经稳定了一段时间,并且所有主要的C ++编译器制造商都支持ANSI标准。
Learning C++
学习C ++最重要的是关注概念。
学习编程语言的目的是成为一个更好的程序员; 也就是说,在设计和实施新系统以及维护旧系统方面变得更加有效。
C ++支持各种编程风格。 您可以用任何语言编写Fortran,C,Smalltalk等风格。 每种风格都能有效地实现其目标,同时保持运行时和空间效率。
使用C ++
几乎每个应用程序域中都有成千上万的程序员使用C ++。
C ++被高度用于编写依赖于在实时约束下直接操作硬件的设备驱动程序和其他软件。
C ++被广泛用于教学和研究,因为它足够干净,可以成功地教授基本概念。
任何使用Apple Macintosh或运行Windows的PC的人都间接使用C ++,因为这些系统的主要用户界面是用C ++编写的。
C++环境搭建 (C++ Environment Setup)
本地环境设置 (Local Environment Setup)
如果您仍然愿意为C ++设置环境,则需要在计算机上安装以下两个软件。
文本编辑器
这将用于键入您的程序。 少数编辑器的示例包括Windows Notepad,OS Edit命令,Brief,Epsilon,EMACS和vim或vi。
文本编辑器的名称和版本可能因不同的操作系统而异。 例如,Notepad将在Windows上使用,vim或vi可以在Windows,Linux或UNIX上使用。
使用编辑器创建的文件称为源文件,对于C ++,它们通常以扩展名.cpp,.cp或.c命名。
应该有一个文本编辑器来启动C ++编程。
C++编译器 (C++ Compiler)
这是一个实际的C ++编译器,它将用于将源代码编译为最终的可执行程序。
大多数C ++编译器都不关心你给你的源代码扩展什么,但是如果你没有另外指定,很多人会默认使用.cpp。
最常用和免费的编译器是GNU C/C ++编译器,否则如果您有相应的操作系统,则可以使用HP或Solaris编译器。
安装GNU C/C ++编译器
UNIX/Linux Installation
如果您使用的是Linux or 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安装
如果您使用的是Mac OS X,获取GCC的最简单方法是从Apple网站下载Xcode开发环境,并按照简单的安装说明进行操作。
Xcode目前可在developer.apple.com/technologies/tools/ 。
Windows安装 (Windows Installation)
要在Windows上安装GCC,您需要安装MinGW。 要安装MinGW,请访问MinGW主页www.mingw.org ,然后点击MinGW下载页面的链接。 下载最新版本的MinGW安装程序,该程序应命名为MinGW-“version”.exe。
在安装MinGW时,至少必须安装gcc-core,gcc-g ++,binutils和MinGW运行时,但您可能希望安装更多。
将MinGW安装的bin子目录添加到PATH环境变量中,以便您可以通过简单名称在命令行上指定这些工具。
安装完成后,您将能够从Windows命令行运行gcc,g ++,ar,ranlib,dlltool和其他几个GNU工具。
C++基本语法 (C++ Basic Syntax)
当我们考虑一个C ++程序时,它可以被定义为通过调用彼此的方法进行通信的对象集合。 现在让我们简要地看一下类,对象,方法和即时变量的含义。
Object - 对象具有状态和行为。 示例:狗有状态 - 颜色,名称,品种以及行为 - 摇摆,吠叫,吃东西。 对象是类的实例。
Class - 可以将类定义为描述其类型对象支持的行为/状态的模板/蓝图。
Methods - 方法基本上是一种行为。 一个类可以包含许多方法。 它是在编写逻辑,操纵数据并执行所有操作的方法中。
Instance Variables - 每个对象都有其唯一的实例变量集。 对象的状态由分配给这些实例变量的值创建。
C++程序结构 (C++ Program Structure)
让我们看一下打印Hello World这个词的简单代码。
#include <iostream>
using namespace std;
// main() is where program execution begins.
int main() {
cout << "Hello World"; // prints Hello World
return 0;
}
让我们看看上述计划的各个部分 -
C ++语言定义了几个标题,其中包含对程序必要或有用的信息。 对于此程序,需要标题《iostream》 。
using namespace std;的行using namespace std; 告诉编译器使用std命名空间。 命名空间是C ++的一个相对新近的补充。
下一行' // main() is where program execution begins. '是C ++中提供的单行注释。 单行注释以//开头,并在行尾停止。
int main()是程序执行开始的主要功能。
下一行cout 《《 "Hello World"; 导致消息“Hello World”显示在屏幕上。
下一行return 0; 终止main()函数并使其将值0返回给调用进程。
编译和执行C ++程序
让我们看看如何保存文件,编译和运行程序。 请按照以下步骤进行操作 -
打开文本编辑器并添加上面的代码。
将文件另存为:hello.cpp
打开命令提示符并转到保存文件的目录。
输入'g ++ hello.cpp'并按Enter键编译代码。 如果代码中没有错误,命令提示符将转到下一行并生成a.out可执行文件。
现在,输入'a.out'来运行您的程序。
您将能够在窗口上看到“Hello World”。
$ g++ hello.cpp
$ ./a.out
Hello World
确保g ++在您的路径中,并且您在包含文件hello.cpp的目录中运行它。
您可以使用makefile编译C/C ++程序。 有关更多详细信息,请查看我们的“Makefile教程” 。
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);
是相同的
x = y; y = y + 1; add(x, y);
C++标识符 (C++ Identifiers)
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++关键字 (C++ Keywords)
以下列表显示了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 |
Trigraphs (Trigraphs)
一些字符具有替代表示,称为三字符序列。 三字母是一个三字符序列,表示单个字符,序列始终以两个问号开头。
Trigraph在它们出现的任何地方都会被扩展,包括字符串文字和字符文字,注释和预处理器指令。
以下是最常用的三字符序列 -
三字母组合 | 替换 |
---|---|
??= | # |
??/ | \ |
??' | ^ |
??( | [ |
??) | ] |
??! | | |
??< | { |
??> | } |
??- | ~ |
所有编译器都不支持三字母组合,因为它们的混乱性质,不建议使用它们。
C ++中的空白
只包含空格(可能带有注释)的行称为空行,C ++编译器完全忽略它。
空格是C ++中用于描述空格,制表符,换行符和注释的术语。 空格将语句的一部分与另一部分分开,并使编译器能够识别语句中的一个元素(如int)的结束位置以及下一个元素的开始位置。
声明1
int age;
在上面的语句中,int和age之间必须至少有一个空格字符(通常是空格),以便编译器能够区分它们。
声明2
fruit = apples + oranges; // Get the total fruit
在上面的语句2中,在fruit和=之间,或者在=和apples之间不需要空白字符,尽管如果您希望出于可读性目的,可以自由地包含一些空格字符。
Comments in C++
程序注释是可以包含在C ++代码中的解释性语句。 这些注释可以帮助任何人阅读源代码。 所有编程语言都允许某种形式的注释。
C ++支持单行和多行注释。 C ++编译器会忽略任何注释中可用的所有字符。
C ++注释以/ *开头,以* /结尾。 例如 -
/* This is a comment */
/* C++ comments can also
* span multiple lines
*/
注释也可以以//开头,延伸到行尾。 例如 -
#include <iostream>
using namespace std;
main() {
cout << "Hello World"; // prints Hello World
return 0;
}
编译上面的代码时,它将忽略// prints Hello World ,最终的可执行文件将产生以下结果 -
Hello World
在/ *和* /注释中,//字符没有特殊含义。 在//注释中,/ *和* /没有特殊含义。 因此,您可以在另一种类型中“嵌套”一种注释。 例如 -
/* Comment out printing of Hello World:
cout << "Hello World"; // prints Hello World
*/
C++数据类型 (C++ Data Types)
在用任何语言编写程序时,您需要使用各种变量来存储各种信息。 变量只是用于存储值的保留内存位置。 这意味着当您创建变量时,您在内存中保留了一些空间。
您可能希望存储各种数据类型的信息,如字符,宽字符,整数,浮点,双浮点,布尔等。根据变量的数据类型,操作系统分配内存并决定可以存储的内容。保留的记忆。
原始内置类型
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到127或0到255 |
unsigned char | 1byte | 0到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 | Range | 0 to 65,535 |
signed short int | Range | -32768 to 32767 |
long int | 4bytes | -2,147,483,648 to 2,147,483,647 |
signed long int | 4bytes | 与long int相同 |
unsigned long int | 4bytes | 0 to 4,294,967,295 |
float | 4bytes | +/- 3.4e +/- 38(~7位) |
double | 8bytes | +/- 1.7e +/- 308(~15位) |
long double | 8bytes | +/- 1.7e +/- 308(~15位) |
wchar_t | 2或4个字节 | 1 wide character |
变量的大小可能与上表中显示的不同,具体取决于您使用的编译器和计算机。
以下是示例,它将在您的计算机上生成各种数据类型的正确大小。
#include <iostream>
using namespace std;
int main() {
cout << "Size of char : " << sizeof(char) << endl;
cout << "Size of int : " << sizeof(int) << endl;
cout << "Size of short int : " << sizeof(short int) << endl;
cout << "Size of long int : " << sizeof(long int) << endl;
cout << "Size of float : " << sizeof(float) << endl;
cout << "Size of double : " << sizeof(double) << endl;
cout << "Size of wchar_t : " << sizeof(wchar_t) << endl;
return 0;
}
此示例使用endl ,它在每行之后插入一个换行符,并使用“”运算符将多个值传递给屏幕。 我们还使用sizeof()运算符来获取各种数据类型的大小。
编译并执行上述代码时,会产生以下结果,这些结果因机器而异 -
Size of char : 1
Size of int : 4
Size of short int : 2
Size of long int : 4
Size of float : 4
Size of double : 8
Size of wchar_t : 4
typedef声明
您可以使用typedef为现有类型创建新名称。 以下是使用typedef定义新类型的简单语法 -
typedef type newname;
例如,以下告诉编译器脚是int的另一个名称 -
typedef int feet;
现在,以下声明完全合法,并创建一个名为distance的整数变量 -
feet distance;
枚举类型
枚举类型声明可选的类型名称和一组零个或多个标识符,可用作该类型的值。 每个枚举器都是一个常量,其类型是枚举。
创建枚举需要使用关键字enum 。 枚举类型的一般形式是 -
enum enum-name { list of names } var-list;
这里,枚举名称是枚举的类型名称。 名称列表以逗号分隔。
例如,以下代码定义了一种称为颜色的颜色枚举和颜色类型的变量c。 最后,c被赋值为“blue”。
enum color { red, green, blue } c;
c = blue;
默认情况下,名字的值为0,第二个名称的值为1,第三个名称的值为2,依此类推。 但您可以通过添加初始化程序来提供名称,特定值。 例如,在以下枚举中, green将具有值5。
enum color { red, green = 5, blue };
这里, blue的值为6,因为每个名称将比其前面的名称大一个。
C++变量类型 (C++ Variable Types)
变量为我们提供了程序可以操作的命名存储。 C ++中的每个变量都有一个特定的类型,它决定了变量内存的大小和布局; 可存储在该内存中的值范围; 以及可以应用于变量的操作集。
变量的名称可以由字母,数字和下划线字符组成。 它必须以字母或下划线开头。 大写和小写字母是不同的,因为C ++区分大小写 -
C ++中有以下基本类型的变量,如上一章所述 -
Sr.No | 类型和描述 |
---|---|
1 | bool 存储值true或false。 |
2 | char 通常是一个八位字节(一个字节)。 这是一个整数类型。 |
3 | int 机器最自然的整数大小。 |
4 | float 单精度浮点值。 |
5 | double 双精度浮点值。 |
6 | void 表示缺少类型。 |
7 | wchar_t 广泛的字符类型。 |
C ++还允许定义各种其他类型的变量,我们将在后续章节中介绍它们,如Enumeration, Pointer, Array, Reference, Data structures,和Classes 。
以下部分将介绍如何定义,声明和使用各种类型的变量。
C ++中的变量定义
变量定义告诉编译器为变量创建的存储位置和数量。 变量定义指定数据类型,并包含该类型的一个或多个变量的列表,如下所示 -
type variable_list;
这里, type必须是有效的C ++数据类型,包括char,w_char,int,float,double,bool或任何用户定义的对象等, variable_list可以包含一个或多个用逗号分隔的标识符名称。 这里显示了一些有效的声明 -
int i, j, k;
char c, ch;
float f, salary;
double d;
线int i, j, k; 声明并定义变量i,j和k; 它指示编译器创建名为i,j和k的int类型的变量。
变量可以在其声明中初始化(分配初始值)。 初始化程序包含一个等号,后跟一个常量表达式,如下所示 -
type variable_name = value;
一些例子是 -
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); 所有其他变量的初始值未定义。
C ++中的变量声明
变量声明为编译器提供了保证,即存在一个具有给定类型和名称的变量,以便编译器继续进行进一步编译,而无需完整的变量详细信息。 变量声明仅在编译时有意义,编译器在链接程序时需要实际的变量定义。
当您使用多个文件并在链接程序时可用的其中一个文件中定义变量时,变量声明很有用。 您将使用extern关键字在任何地方声明变量。 虽然您可以在C ++程序中多次声明变量,但它只能在文件,函数或代码块中定义一次。
例子 (Example)
尝试以下示例,其中变量已在顶部声明,但已在主函数内定义 -
#include <iostream>
using namespace std;
// Variable declaration:
extern int a, b;
extern int c;
extern float f;
int main () {
// Variable definition:
int a, b;
int c;
float f;
// actual initialization
a = 10;
b = 20;
c = a + b;
cout << c << endl ;
f = 70.0/3.0;
cout << f << endl ;
return 0;
}
编译并执行上述代码时,会产生以下结果 -
30
23.3333
同样的概念适用于函数声明,其中您在声明时提供函数名称,并且可以在其他任何位置给出其实际定义。 例如 -
// function declaration
int func();
int main() {
// function call
int i = func();
}
// function definition
int func() {
return 0;
}
左值和右值
C ++中有两种表达式 -
lvalue - 引用内存位置的表达式称为“左值”表达式。 左值可以显示为赋值的左侧或右侧。
rvalue - 术语rvalue是指存储在内存中某个地址的数据值。 rvalue是一个不能赋值给它的表达式,这意味着rvalue可能出现在赋值的右边但不是左边。
变量是左值,因此可能出现在赋值的左侧。 数字文字是右值,因此可能无法分配,也不能出现在左侧。 以下是有效的声明 -
int g = 20;
但以下不是有效的语句,会产生编译时错误 -
10 = 20;
Variable Scope in C++
范围是程序的一个区域,从广义上讲,有三个地方,可以声明变量 -
在函数或块中称为局部变量,
在函数参数的定义中称为形式参数。
在所有函数之外,称为全局变量。
我们将在后续章节中学习什么是函数及其参数。 这里让我们解释什么是局部变量和全局变量。
局部变量 (Local Variables)
在函数或块内声明的变量是局部变量。 它们只能由该函数或代码块中的语句使用。 本地变量不为其自身以外的函数所知。 以下是使用局部变量的示例 -
#include <iostream>
using namespace std;
int main () {
// Local variable declaration:
int a, b;
int c;
// actual initialization
a = 10;
b = 20;
c = a + b;
cout << c;
return 0;
}
全局变量 (Global Variables)
全局变量在所有函数之外定义,通常在程序之上。 全局变量将在程序的整个生命周期中保持其价值。
任何函数都可以访问全局变量。 也就是说,全局变量在声明后可用于整个程序。 以下是使用全局变量和局部变量的示例 -
#include <iostream>
using namespace std;
// Global variable declaration:
int g;
int main () {
// Local variable declaration:
int a, b;
// actual initialization
a = 10;
b = 20;
g = a + b;
cout << g;
return 0;
}
程序对于局部变量和全局变量可以具有相同的名称,但函数内的局部变量值将优先考虑。 例如 -
#include <iostream>
using namespace std;
// Global variable declaration:
int g = 20;
int main () {
// Local variable declaration:
int g = 10;
cout << g;
return 0;
}
编译并执行上述代码时,会产生以下结果 -
10
初始化本地和全局变量
定义局部变量时,系统不会初始化它,您必须自己初始化它。 当您按如下方式定义全局变量时,系统会自动初始化全局变量 -
数据类型 | 初始化 |
---|---|
int | 0 |
char | '\0' |
float | 0 |
double | 0 |
pointer | NULL |
正确初始化变量是一种很好的编程习惯,否则程序会产生意外的结果。
C++字面常量 (C++ Constants/Literals)
常量指的是程序可能不会改变的固定值,它们被称为literals 。
常量可以是任何基本数据类型,可以分为整数,浮点数,字符,字符串和布尔值。
同样,常量被视为常规变量,除了它们的值在定义后无法修改。
整型常量 (Integer Literals)
整数文字可以是十进制,八进制或十六进制常量。 前缀指定基数或基数:十六进制为0x或0X,八进制为0,十进制为空。
整数文字也可以有一个后缀,它是U和L的组合,分别对于unsigned和long。 后缀可以是大写或小写,可以按任何顺序排列。
以下是整数文字的一些示例 -
212 // Legal
215u // Legal
0xFeeL // Legal
078 // Illegal: 8 is not an octal digit
032UU // Illegal: cannot repeat a suffix
以下是各种类型的整数文字的其他示例 -
85 // decimal
0213 // octal
0x4b // hexadecimal
30 // int
30u // unsigned int
30l // long
30ul // unsigned long
浮点型常量 (Floating-point Literals)
浮点文字有一个整数部分,一个小数点,一个小数部分和一个指数部分。 您可以以十进制形式或指数形式表示浮点文字。
在使用小数形式表示时,必须包括小数点,指数或两者,并且在使用指数形式表示时,必须包括整数部分,小数部分或两者。 带符号的指数由e或E引入。
以下是浮点文字的一些示例 -
3.14159 // Legal
314159E-5L // Legal
510E // Illegal: incomplete exponent
210f // Illegal: no decimal or exponent
.e55 // Illegal: missing integer or fraction
布尔文字
有两个布尔文字,它们是标准C ++关键字的一部分 -
值true表示true。
值false表示false。
您不应该将true的值等于1,将false值等于0。
字符文字
字符文字用单引号括起来。 如果文字以L开头(仅大写),则它是宽字符文字(例如,L'x')并且应存储在wchar_t类型的变量中。 否则,它是一个窄字符文字(例如,'x'),并且可以存储在char类型的简单变量中。
字符文字可以是普通字符(例如,'x'),转义序列(例如,'\ t'),或通用字符(例如,'\ u02C0')。
C ++中有某些字符,如果它们前面有反斜杠,它们将具有特殊含义,它们用于表示换行符(\ n)或制表符(\ t)。 在这里,您有一些此类转义序列代码的列表 -
逃脱序列 | 含义 |
---|---|
\\ | \字符 |
\' | ' character(字符) |
\" | " character(字符) |
\? | ? 字符 |
\a | Alert or bell |
\b | Backspace |
\f | Form feed |
\n | Newline |
\r | Carriage return |
\t | 水平标签 |
\v | 垂直标签 |
\ooo | 八进制数字为一到三位数 |
\xhh。 。 。 | 十六进制数字的一个或多个数字 |
以下是显示一些转义序列字符的示例 -
#include <iostream>
using namespace std;
int main() {
cout << "Hello\tWorld\n\n";
return 0;
}
编译并执行上述代码时,会产生以下结果 -
Hello World
字符串常量 (String Literals)
字符串文字用双引号括起来。 字符串包含与字符文字类似的字符:普通字符,转义序列和通用字符。
您可以使用字符串文字将长行分成多行,并使用空格分隔它们。
以下是字符串文字的一些示例。 所有三种形式都是相同的字符串。
"hello, dear"
"hello, \
dear"
"hello, " "d" "ear"
定义常量
C ++中有两种简单的方法来定义常量 -
使用#define预处理器。
使用const关键字。
#define预处理器 (The #define Preprocessor)
以下是使用#define预处理器定义常量的表单 -
#define identifier value
以下示例详细解释 -
#include <iostream>
using namespace std;
#define LENGTH 10
#define WIDTH 5
#define NEWLINE '\n'
int main() {
int area;
area = LENGTH * WIDTH;
cout << area;
cout << NEWLINE;
return 0;
}
编译并执行上述代码时,会产生以下结果 -
50
const关键字
您可以使用const前缀来声明具有特定类型的常量,如下所示 -
const type variable = value;
以下示例详细解释 -
#include <iostream>
using namespace std;
int main() {
const int LENGTH = 10;
const int WIDTH = 5;
const char NEWLINE = '\n';
int area;
area = LENGTH * WIDTH;
cout << area;
cout << NEWLINE;
return 0;
}
编译并执行上述代码时,会产生以下结果 -
50
请注意,在CAPITALS中定义常量是一种很好的编程习惯。
C++修饰符类型 (C++ Modifier Types)
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 ++解释有符号和无符号整数修饰符的方式之间的区别,您应该运行以下短程序 -
#include <iostream>
using namespace std;
/* This program shows the difference between
* signed and unsigned integers.
*/
int main() {
short int i; // a signed short integer
short unsigned int j; // an unsigned short integer
j = 50000;
i = j;
cout << i << " " << j;
return 0;
}
运行此程序时,以下是输出 -
-15536 50000
上述结果是因为表示50,000作为短无符号整数的位模式被短路解释为-15,536。
在C ++中键入限定符
类型限定符提供有关它们之前的变量的其他信息。
Sr.No | 限定词和含义 |
---|---|
1 | const 执行期间程序无法更改const类型的对象。 |
2 | volatile 修饰符volatile告诉编译器变量的值可能会以程序未明确指定的方式更改。 |
3 | restrict 由restrict限定的指针最初是可以访问它指向的对象的唯一方法。 只有C99添加了一个名为restrict的新类型限定符。 |
Storage Classes in C++
存储类定义C ++程序中变量和/或函数的范围(可见性)和生命周期。 这些说明符位于它们修改的类型之前。 有以下存储类,可以在C ++程序中使用
- auto
- register
- static
- extern
- mutable
自动存储类
auto存储类是所有局部变量的默认存储类。
{
int mount;
auto int month;
}
上面的例子定义了两个具有相同存储类的变量,auto只能在函数中使用,即局部变量。
寄存器存储类
register存储类用于定义应存储在寄存器而不是RAM中的局部变量。 这意味着变量的最大大小等于寄存器大小(通常是一个单词),并且不能将一元“&”运算符应用于它(因为它没有内存位置)。
{
register int miles;
}
该寄存器只应用于需要快速访问的变量,例如计数器。 还应注意,定义“寄存器”并不意味着变量将存储在寄存器中。 这意味着它可能会根据硬件和实现限制存储在寄存器中。
静态存储类
static存储类指示编译器在程序的生命周期内保留一个局部变量,而不是在每次进入和超出范围时创建和销毁它。 因此,使局部变量静态允许它们在函数调用之间维护它们的值。
静态修饰符也可以应用于全局变量。 完成此操作后,它会将该变量的范围限制为声明它的文件。
在C ++中,当在类数据成员上使用static时,它只会导致该成员的所有对象共享该成员的一个副本。
#include <iostream>
// Function declaration
void func(void);
static int count = 10; /* Global variable */
main() {
while(count--) {
func();
}
return 0;
}
// Function definition
void func( void ) {
static int i = 5; // local static variable
i++;
std::cout << "i is " << i ;
std::cout << " and count is " << count << std::endl;
}
编译并执行上述代码时,会产生以下结果 -
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用于在另一个文件中声明全局变量或函数。
当有两个或多个文件共享相同的全局变量或函数时,最常用的是外部修饰符,如下所述。
第一个文件:main.cpp
#include <iostream>
int count ;
extern void write_extern();
main() {
count = 5;
write_extern();
}
第二个文件:support.cpp
#include <iostream>
extern int count;
void write_extern(void) {
std::cout << "Count is " << count << std::endl;
}
这里, extern关键字用于在另一个文件中声明count。 现在编译这两个文件如下 -
$g++ main.cpp support.cpp -o write
这将产生write可执行程序,尝试执行write并检查结果如下 -
$./write
5
可变存储类
mutable说明符仅适用于类对象,本教程稍后将对此进行讨论。 它允许对象的成员覆盖const成员函数。 也就是说,可以通过const成员函数修改可变成员。
Operators in C++
运算符是一个符号,告诉编译器执行特定的数学或逻辑操作。 C ++内置丰富的运算符,并提供以下类型的运算符 -
- 算术运算符
- 关系运算符
- 逻辑运算符
- 按位运算符
- 分配运算符
- 其它运算符
本章将逐一检查算术,关系,逻辑,按位,赋值和其他运算符。
算术运算符 (Arithmetic Operators)
C ++语言支持以下算术运算符 -
假设变量A保持10,变量B保持20,则 -
操作者 | 描述 | 例 |
---|---|---|
+ | 添加两个操作数 | A + B将给出30 |
- | 从第一个减去第二个操作数 | A - B将给-10 |
* | 将两个操作数相乘 | A * B将给出200 |
/ | Divides numerator by de-numerator | B/A会给2 |
% | 模数运算符和整数除法后的余数 | B%A将给出0 |
++ | 递增运算符 ,将整数值增加1 | A ++将给出11 |
-- | 递减运算符 ,将整数值减一 | A-- will give 9 |
关系运算符 (Relational Operators)
C ++语言支持以下关系运算符
假设变量A保持10,变量B保持20,则 -
操作者 | 描述 | 例 |
---|---|---|
== | 检查两个操作数的值是否相等,如果是,则条件变为真。 | (A == B)不是真的。 |
!= | 检查两个操作数的值是否相等,如果值不相等则条件变为真。 | (A!= B)是真的。 |
> | 检查左操作数的值是否大于右操作数的值,如果是,则条件变为真。 | (A> B)不是真的。 |
< | 检查左操作数的值是否小于右操作数的值,如果是,则条件变为真。 | (A < B) 为真 |
>= | 检查左操作数的值是否大于或等于右操作数的值,如果是,则条件变为真。 | (A> = B)不是真的。 |
<= | 检查左操作数的值是否小于或等于右操作数的值,如果是,则条件变为真。 | (A <= B)是真的。 |
逻辑运算符 (Logical Operators)
C ++语言支持以下逻辑运算符。
假设变量A保持1而变量B保持0,则 -
操作者 | 描述 | 例 |
---|---|---|
&& | 称为逻辑AND运算符。 如果两个操作数都不为零,则条件成立。 | (A && B)是假的。 |
|| | 称为逻辑OR运算符。 如果两个操作数中的任何一个非零,则条件变为真。 | (A || B)是真的。 |
! | 称为逻辑非运算符。 用于反转其操作数的逻辑状态。 如果条件为真,则Logical NOT运算符将为false。 | !(A && B)是真的。 |
按位运算符 (Bitwise Operators)
按位运算符处理位并执行逐位运算。 &,|和^的真值表如下 -
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 |
假设A = 60; 和B = 13; 现在以二进制格式,他们将如下 -
A = 0011 1100
B = 0000 1101
-----------------
A&B = 0000 1100
A | B = 0011 1101
A ^ B = 0011 0001
~A = 1100 0011
下表列出了C ++语言支持的Bitwise运算符。 假设变量A保持60,变量B保持13,则 -
操作者 | 描述 | 例 |
---|---|---|
& | 如果二进制AND运算符存在于两个操作数中,则它会将结果复制到结果中。 | (A&B)将给出12,即0000 1100 |
| | 二进制OR运算符如果存在于任一操作数中,则复制一位。 | (A | B)将给出61,即0011 1101 |
^ | 二进制异或运算符如果在一个操作数中设置但不在两个操作数中设置,则复制该位。 | (A ^ B)将给出49,即0011 0001 |
~ | 二元一元补语运算符是一元的,具有“翻转”位的效果。 | 由于带符号的二进制数,(〜A)将给出-61,即2的补码形式的1100 0011。 |
<< | 二进制左移运算符。 左操作数值向左移动右操作数指定的位数。 | A << 2将给出240,即1111 0000 |
>> | 二进制右移运算符。 左操作数值向右移动右操作数指定的位数。 | A >> 2将给出15,即0000 1111 |
赋值操作符 (Assignment Operators)
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相同 |
&= | 按位AND赋值运算符。 | C&= 2与C = C&2相同 |
^= | 按位异或和赋值运算符。 | C ^ = 2与C = C ^ 2相同 |
|= | 按位包含OR和赋值运算符。 | C | = 2与C = C |相同 2 |
其它运算符
下表列出了C ++支持的其他一些运算符。
Sr.No | 操作符和说明 |
---|---|
1 | sizeof sizeof运算符返回变量的大小。 例如,sizeof(a),其中'a'是整数,并将返回4。 |
2 | Condition ? X : Y 条件运算符(?) 。 如果Condition为true,则返回X的值,否则返回Y的值。 |
3 | , 逗号运算符会导致执行一系列操作。 整个逗号表达式的值是以逗号分隔的列表的最后一个表达式的值。 |
4 | . (dot) and -》 (arrow) 成员运算符用于引用类,结构和联合的各个成员。 |
5 | Cast 转换运算符将一种数据类型转换为另一种数据 例如,int(2.2000)将返回2。 |
6 | & 指针运算符&返回变量的地址。 例如&a; 将给出变量的实际地址。 |
7 | * 指针运算符*是指向变量的指针。 例如* var; 将指向变量var。 |
运算符在C ++中的优先级
运算符优先级确定表达式中的术语分组。 这会影响表达式的计算方式。 某些运算符的优先级高于其他运算符; 例如,乘法运算符的优先级高于加法运算符 -
例如x = 7 + 3 * 2; 这里,x被赋值为13,而不是20,因为operator *的优先级高于+,所以它首先乘以3 * 2然后加到7中。
此处,具有最高优先级的运算符显示在表的顶部,具有最低优先级的运算符显示在底部。 在表达式中,将首先评估更高优先级的运算符。
类别 | 操作者 | 关联性 |
---|---|---|
Postfix | ()[] - >。 ++ - - | 左到右 |
Unary | + - ! 〜++ - - (类型)*&sizeof | 右到左 |
Multiplicative | * /% | 左到右 |
Additive | + - | 左到右 |
Shift | << >> | 左到右 |
Relational | << => = | 左到右 |
Equality | ==!= | 左到右 |
Bitwise AND | & | 左到右 |
Bitwise XOR | ^ | 左到右 |
Bitwise OR | | | 左到右 |
Logical AND | && | 左到右 |
Logical OR | || | 左到右 |
Conditional | ?: | 右到左 |
Assignment | = + = - = * =/=%= >> = << =&= ^ = | = | 右到左 |
Comma | , | 左到右 |
C++ Loop Types
当您需要多次执行代码块时,可能会出现这种情况。 通常,语句按顺序执行:首先执行函数中的第一个语句,然后执行第二个语句,依此类推。
编程语言提供各种控制结构,允许更复杂的执行路径。
循环语句允许我们多次执行一个语句或一组语句,以下是大多数编程语言中循环语句的一般说法 -
C ++编程语言提供以下类型的循环来处理循环要求。
Sr.No | 循环类型和描述 |
---|---|
1 | 循环 在给定条件为真时重复语句或语句组。 它在执行循环体之前测试条件。 |
2 | for循环 多次执行一系列语句,并缩写管理循环变量的代码。 |
3 | 做... while循环 像'while'语句一样,除了它测试循环体末端的条件。 |
4 | 嵌套循环 您可以在任何其他'while','for'或'do..while'循环中使用一个或多个循环。 |
循环控制语句 (Loop Control Statements)
循环控制语句将执行从其正常序列更改。 当执行离开作用域时,将销毁在该作用域中创建的所有自动对象。
C ++支持以下控制语句。
Sr.No | 控制声明和描述 |
---|---|
1 | break statement 终止loop或switch语句,并在循环或切换后立即将执行转移到语句。 |
2 | continue statement 导致循环跳过其身体的其余部分,并在重复之前立即重新测试其状态。 |
3 | goto statement 将控制转移到带标签的声明。 虽然不建议在程序中使用goto语句。 |
无限循环 (The Infinite Loop)
如果条件永远不会变为假,则循环变为无限循环。 for循环传统上用于此目的。 由于不需要构成'for'循环的三个表达式,因此可以通过将条件表达式留空来创建无限循环。
#include <iostream>
using namespace std;
int main () {
for( ; ; ) {
printf("This loop will run forever.\n");
}
return 0;
}
当条件表达式不存在时,假定为真。 您可能有一个初始化和增量表达式,但C ++程序员更常使用'for(;;)'结构来表示无限循环。
NOTE - 您可以通过按Ctrl + C键终止无限循环。
C++ decision making statements
决策结构要求程序员指定一个或多个要由程序评估或测试的条件,以及在条件被确定为真时要执行的一个或多个语句,以及可选的,如果条件要执行的其他语句被认定是假的。
以下是大多数编程语言中常见决策结构的一般形式 -
C ++编程语言提供以下类型的决策制定语句。
Sr.No | 声明和说明 |
---|---|
1 | if 语句 'if'语句由一个布尔表达式后跟一个或多个语句组成。 |
2 | if...else 语句 'if'语句后面可以跟一个可选的'else'语句,该语句在布尔表达式为false时执行。 |
3 | switch 语句 'switch'语句允许根据值列表测试变量的相等性。 |
4 | 嵌套if语句 您可以在另一个“if”或“else if”语句中使用一个“if”或“else if”语句。 |
5 | 嵌套的switch语句 您可以在另一个'switch'语句中使用一个'switch'语句。 |
(The ? : Operator)
我们已经涵盖了条件运算符“? :“在前一章中可以用来替换if...else语句。 它有以下一般形式 -
Exp1 ? Exp2 : Exp3;
Exp1,Exp2和Exp3是表达式。 注意结肠的使用和放置。
'?'的价值 表达式的确定方式如下:评估Exp1。 如果是,则评估Exp2并成为整个'?'的值 表达。 如果Exp1为false,则计算Exp3,其值将成为表达式的值。
C++ Functions
函数是一组一起执行任务的语句。 每个C ++程序至少有一个函数,即main() ,所有最简单的程序都可以定义其他函数。
您可以将代码划分为单独的函数。 如何在不同的函数之间划分代码取决于您,但从逻辑上讲,除法通常是每个函数执行特定任务。
函数declaration告诉编译器函数的名称,返回类型和参数。 函数definition提供函数的实际主体。
C ++标准库提供了许多程序可以调用的内置函数。 例如,函数strcat()连接两个字符串,函数memcpy()将一个内存位置复制到另一个位置以及更多函数。
已知具有各种名称的函数,例如方法或子例程或过程等。
定义一个函数 (Defining a Function)
C ++函数定义的一般形式如下 -
return_type function_name( parameter list ) {
body of the function
}
C ++函数定义由函数头和函数体组成。 以下是函数的所有部分 -
Return Type - 函数可以返回值。 return_type是函数返回的值的数据类型。 某些函数执行所需的操作而不返回值。 在这种情况下,return_type是关键字void 。
Function Name - 这是Function Name的实际名称。 函数名称和参数列表一起构成函数签名。
Parameters - 参数类似于占位符。 调用函数时,将值传递给参数。 该值称为实际参数或参数。 参数列表是指函数参数的类型,顺序和数量。 参数是可选的; 也就是说,函数可能不包含任何参数。
Function Body - 函数体包含一组语句,用于定义函数的功能。
例子 (Example)
以下是名为max()的函数的源代码。 这个函数有两个参数num1和num2,并返回两个参数中最大的 -
// function returning the max between two numbers
int max(int num1, int num2) {
// local variable declaration
int result;
if (num1 > num2)
result = num1;
else
result = num2;
return result;
}
函数声明 (Function Declarations)
函数declaration告诉编译器函数名称以及如何调用函数。 函数的实际主体可以单独定义。
功能声明包含以下部分 -
return_type function_name( parameter list );
对于上面定义的函数max(),以下是函数声明 -
int max(int num1, int num2);
参数名称在函数声明中并不重要,只需要它们的类型,因此以下也是有效的声明 -
int max(int, int);
在一个源文件中定义函数并在另一个文件中调用该函数时,需要函数声明。 在这种情况下,您应该在调用该函数的文件顶部声明该函数。
调用一个函数 (Calling a Function)
在创建C ++函数时,您可以定义函数必须执行的操作。 要使用函数,您必须调用或调用该函数。
当程序调用一个函数时,程序控制被转移到被调用的函数。 被调用函数执行已定义的任务,当执行返回语句或达到其函数结束右括号时,它将程序控制返回给主程序。
要调用函数,只需要传递必需的参数和函数名称,如果函数返回一个值,那么您可以存储返回的值。 例如 -
#include <iostream>
using namespace std;
// function declaration
int max(int num1, int num2);
int main () {
// local variable declaration:
int a = 100;
int b = 200;
int ret;
// calling a function to get max value.
ret = max(a, b);
cout << "Max value is : " << ret << endl;
return 0;
}
// function returning the max between two numbers
int max(int num1, int num2) {
// local variable declaration
int result;
if (num1 > num2)
result = num1;
else
result = num2;
return result;
}
我保持max()函数和main()函数并编译源代码。 在运行最终可执行文件时,它会产生以下结果 -
Max value is : 200
函数参数(Function Arguments)
如果函数是使用参数,它必须声明接受参数值的变量。 这些变量称为函数的formal parameters 。
形式参数的行为与函数内部的其他局部变量相似,并在进入函数时创建,并在退出时销毁。
在调用函数时,有两种方法可以将参数传递给函数 -
Sr.No | 通话类型和说明 |
---|---|
1 | call by value [值传递] 此方法将参数的实际值复制到函数的形式参数中。 在这种情况下,对函数内部参数所做的更改不会对参数产生影响。 |
2 | 通过指针调用 此方法将参数的地址复制到形式参数中。 在函数内部,该地址用于访问调用中使用的实际参数。 这意味着对参数所做的更改会影响参数。 |
3 | 按参考呼叫 此方法将参数的引用复制到形式参数中。 在函数内部,引用用于访问调用中使用的实际参数。 这意味着对参数所做的更改会影响参数。 |
默认情况下,C ++使用call by value来传递参数。 通常,这意味着函数内的代码不能改变用于调用函数的参数和上面提到的示例,而调用max()函数则使用相同的方法。
参数的默认值
定义函数时,可以为每个最后一个参数指定默认值。 如果在调用函数时相应的参数保留为空,则将使用此值。
这是通过使用赋值运算符并为函数定义中的参数赋值来完成的。 如果在调用函数时未传递该参数的值,则使用默认的给定值,但如果指定了值,则忽略此默认值,而是使用传递的值。 考虑以下示例 -
#include <iostream>
using namespace std;
int sum(int a, int b = 20) {
int result;
result = a + b;
return (result);
}
int main () {
// local variable declaration:
int a = 100;
int b = 200;
int result;
// calling a function to add the values.
result = sum(a, b);
cout << "Total value is :" << result << endl;
// calling a function again as follows.
result = sum(a);
cout << "Total value is :" << result << endl;
return 0;
}
编译并执行上述代码时,会产生以下结果 -
Total value is :300
Total value is :120
Numbers in C++
通常,当我们使用Numbers时,我们使用原始数据类型,如int,short,long,float和double等。在讨论C ++数据类型时,已经解释了数字数据类型,它们的可能值和数字范围。
用C ++定义数字
您已经在前面章节中给出的各种示例中定义了数字。 这是在C ++中定义各种类型数字的另一个合并示例 -
#include <iostream>
using namespace std;
int main () {
// number definition:
short s;
int i;
long l;
float f;
double d;
// number assignments;
s = 10;
i = 1000;
l = 1000000;
f = 230.47;
d = 30949.374;
// number printing;
cout << "short s :" << s << endl;
cout << "int i :" << i << endl;
cout << "long l :" << l << endl;
cout << "float f :" << f << endl;
cout << "double d :" << d << endl;
return 0;
}
编译并执行上述代码时,会产生以下结果 -
short s :10
int i :1000
long l :1000000
float f :230.47
double d :30949.4
C ++中的数学运算
除了可以创建的各种功能外,C ++还包括一些您可以使用的有用功能。 这些函数在标准C和C ++库中可用,并称为built-in函数。 这些功能可以包含在您的程序中然后使用。
C ++有一组丰富的数学运算,可以在各种数字上执行。 下表列出了C ++中可用的一些有用的内置数学函数。
要使用这些函数,您需要包含数学头文件《cmath》 。
Sr.No | 功能与目的 |
---|---|
1 | double cos(double); 此函数采用一个角度(作为double)并返回余弦。 |
2 | double sin(double); 此函数采用一个角度(作为double)并返回正弦。 |
3 | double tan(double); 此函数采用一个角度(作为double)并返回切线。 |
4 | double log(double); 此函数接受一个数字并返回该数字的自然日志。 |
5 | double pow(double, double); 第一个是你想要提出的数字,第二个是你希望提高的数字 |
6 | double hypot(double, double); 如果您将此函数传递给直角三角形的两边长度,它将返回斜边的长度。 |
7 | double sqrt(double); 你传递这个函数一个数字,它给你平方根。 |
8 | int abs(int); 此函数返回传递给它的整数的绝对值。 |
9 | double fabs(double); 此函数返回传递给它的任何十进制数的绝对值。 |
10 | double floor(double); 查找小于或等于传递给它的参数的整数。 |
以下是一个简单的例子来展示几个数学运算 -
#include <iostream>
#include <cmath>
using namespace std;
int main () {
// number definition:
short s = 10;
int i = -1000;
long l = 100000;
float f = 230.47;
double d = 200.374;
// mathematical operations;
cout << "sin(d) :" << sin(d) << endl;
cout << "abs(i) :" << abs(i) << endl;
cout << "floor(d) :" << floor(d) << endl;
cout << "sqrt(f) :" << sqrt(f) << endl;
cout << "pow( d, 2) :" << pow(d, 2) << endl;
return 0;
}
编译并执行上述代码时,会产生以下结果 -
sign(d) :-0.634939
abs(i) :1000
floor(d) :200
sqrt(f) :15.1812
pow( d, 2 ) :40149.7
C ++中的随机数
在许多情况下,您希望生成随机数。 实际上,您需要了解有关随机数生成的两个函数。 第一个是rand() ,这个函数只返回一个伪随机数。 解决这个问题的方法是先调用srand()函数。
以下是生成少量随机数的简单示例。 这个例子利用time()函数来获取系统时间的秒数,随机播种rand()函数 -
#include <iostream>
#include <ctime>
#include <cstdlib>
using namespace std;
int main () {
int i,j;
// set the seed
srand( (unsigned)time( NULL ) );
/* generate 10 random numbers. */
for( i = 0; i < 10; i++ ) {
// generate actual random number
j = rand();
cout <<" Random Number : " << j << endl;
}
return 0;
}
编译并执行上述代码时,会产生以下结果 -
Random Number : 1748144778
Random Number : 630873888
Random Number : 2134540646
Random Number : 219404170
Random Number : 902129458
Random Number : 920445370
Random Number : 1319072661
Random Number : 257938873
Random Number : 1256201101
Random Number : 580322989
C++ Arrays
C ++提供了一个数据结构, the array ,它存储了相同类型元素的固定大小顺序集合。 数组用于存储数据集合,但将数组视为相同类型的变量集合通常更有用。
您可以声明一个数组变量(例如数字)并使用数字[0],数字[1]和...,数字[99]来表示单个变量,例如number0,number1,...和number99,而不是声明单个变量。个别变数。 索引访问数组中的特定元素。
所有阵列都包含连续的内存位置。 最低地址对应于第一个元素,最高地址对应于最后一个元素。
声明数组 (Declaring Arrays)
要在C ++中声明一个数组,程序员指定元素的类型和数组所需的元素数量,如下所示 -
type arrayName [ arraySize ];
这称为单维数组。 arraySize必须是大于零的整数常量, type可以是任何有效的C ++数据类型。 例如,要声明一个名为balance double类型的10元素数组,请使用此语句 -
double balance[10];
初始化数组 (Initializing Arrays)
您可以逐个或使用单个语句初始化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;
上面的语句在数组中为元素编号5指定值50.0。 具有第 4 个索引的数组将是第 5 个 ,即最后一个元素,因为所有数组都将0作为其第一个元素的索引,也称为基本索引。 以下是我们上面讨论的相同阵列的图形表示 -
访问数组元素 (Accessing Array Elements)
通过索引数组名称来访问元素。 这是通过将元素的索引放在数组名称后面的方括号中来完成的。 例如 -
double salary = balance[9];
上面的语句将从数组中取出第 10 个元素,并将值赋给salary变量。 以下是一个例子,它将使用所有上述三个概念即。 声明,分配和访问数组 -
#include <iostream>
using namespace std;
#include <iomanip>
using std::setw;
int main () {
int n[ 10 ]; // n is an array of 10 integers
// initialize elements of array n to 0
for ( int i = 0; i < 10; i++ ) {
n[ i ] = i + 100; // set element at location i to i + 100
}
cout << "Element" << setw( 13 ) << "Value" << endl;
// output each array element's value
for ( int j = 0; j < 10; j++ ) {
cout << setw( 7 )<< j << setw( 13 ) << n[ j ] << endl;
}
return 0;
}
该程序使用setw()函数格式化输出。 编译并执行上述代码时,会产生以下结果 -
Element Value
0 100
1 101
2 102
3 103
4 104
5 105
6 106
7 107
8 108
9 109
C ++中的数组
数组对C ++很重要,需要更多细节。 以下几个重要概念,C ++程序员应该清楚 -
Sr.No | 概念与描述 |
---|---|
1 | 多维数组 C ++支持多维数组。 多维数组的最简单形式是二维数组。 |
2 | 指向数组的指针 您只需指定数组名称即可生成指向数组第一个元素的指针,而无需任何索引。 |
3 | 将数组传递给函数 您可以通过指定不带索引的数组名称来向函数传递指向数组的指针。 |
4 | 从函数返回数组 C ++允许函数返回一个数组。 |
C++ Strings
C ++提供以下两种类型的字符串表示 -
- C风格的字符串。
- 标准C ++引入的字符串类类型。
C风格的字符串
C风格的字符串起源于C语言,并继续在C ++中得到支持。 该字符串实际上是一维字符数组,由null字符'\ 0'终止。 因此,以null结尾的字符串包含组成字符串后跟null 。
以下声明和初始化创建一个由单词“Hello”组成的字符串。 要将空字符保存在数组的末尾,包含字符串的字符数组的大小比单词“Hello”中的字符数多一个。
char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
如果遵循数组初始化规则,那么可以按如下方式编写上述语句 -
char greeting[] = "Hello";
以下是C/C ++中上面定义的字符串的内存表示 -
实际上,您不会将空字符放在字符串常量的末尾。 初始化数组时,C ++编译器会自动将'\ 0'放在字符串的末尾。 让我们尝试打印上面提到的字符串 -
#include <iostream>
using namespace std;
int main () {
char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
cout << "Greeting message: ";
cout << greeting << endl;
return 0;
}
编译并执行上述代码时,会产生以下结果 -
Greeting message: Hello
C ++支持各种操作以null结尾的字符串的函数 -
Sr.No | 功能与目的 |
---|---|
1 | strcpy(s1, s2); 将字符串s2复制到字符串s1中。 |
2 | strcat(s1, s2); 将字符串s2连接到字符串s1的末尾。 |
3 | strlen(s1); 返回字符串s1的长度。 |
4 | strcmp(s1, s2); 如果s1和s2相同则返回0; 如果s1 |
5 | strchr(s1, ch); 返回指向字符串s1中第一个出现的字符ch的指针。 |
6 | strstr(s1, s2); 返回指向字符串s1中第一次出现的字符串s2的指针。 |
以下示例使用了上述几个函数 -
#include <iostream>
#include <cstring>
using namespace std;
int main () {
char str1[10] = "Hello";
char str2[10] = "World";
char str3[10];
int len ;
// copy str1 into str3
strcpy( str3, str1);
cout << "strcpy( str3, str1) : " << str3 << endl;
// concatenates str1 and str2
strcat( str1, str2);
cout << "strcat( str1, str2): " << str1 << endl;
// total lenghth of str1 after concatenation
len = strlen(str1);
cout << "strlen(str1) : " << len << endl;
return 0;
}
编译和执行上面的代码时,它产生的结果如下 -
strcpy( str3, str1) : Hello
strcat( str1, str2): HelloWorld
strlen(str1) : 10
C ++中的String类
标准C ++库提供了一个string类类型,它支持上面提到的所有操作,还有更多的功能。 让我们检查以下示例 -
#include <iostream>
#include <string>
using namespace std;
int main () {
string str1 = "Hello";
string str2 = "World";
string str3;
int len ;
// copy str1 into str3
str3 = str1;
cout << "str3 : " << str3 << endl;
// concatenates str1 and str2
str3 = str1 + str2;
cout << "str1 + str2 : " << str3 << endl;
// total length of str3 after concatenation
len = str3.size();
cout << "str3.size() : " << len << endl;
return 0;
}
编译和执行上面的代码时,它产生的结果如下 -
str3 : Hello
str1 + str2 : HelloWorld
str3.size() : 10
C++ Pointers
C ++指针很容易学习。 使用指针可以更轻松地执行某些C ++任务,而没有它们就无法执行其他C ++任务,例如动态内存分配。
如您所知,每个变量都是一个内存位置,并且每个内存位置都定义了其地址,可以使用&符号(&)运算符来访问它,该运算符表示内存中的地址。 考虑以下将打印定义的变量的地址 -
#include <iostream>
using namespace std;
int main () {
int var1;
char var2[10];
cout << "Address of var1 variable: ";
cout << &var1 << endl;
cout << "Address of var2 variable: ";
cout << &var2 << endl;
return 0;
}
编译并执行上述代码时,会产生以下结果 -
Address of var1 variable: 0xbfebd5c0
Address of var2 variable: 0xbfebd5b6
什么是指针 (What are Pointers?)
pointer是一个变量,其值是另一个变量的地址。 与任何变量或常量一样,您必须先声明指针才能使用它。 指针变量声明的一般形式是 -
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)最后访问指针变量中可用地址的值。 这是通过使用一元运算符*来完成的,该运算符*返回位于其操作数指定的地址处的变量的值。 以下示例使用这些操作 -
#include <iostream>
using namespace std;
int main () {
int var = 20; // actual variable declaration.
int *ip; // pointer variable
ip = &var; // store address of var in pointer variable
cout << "Value of var variable: ";
cout << var << endl;
// print the address stored in ip pointer variable
cout << "Address stored in ip variable: ";
cout << ip << endl;
// access the value at the address available in pointer
cout << "Value of *ip variable: ";
cout << *ip << endl;
return 0;
}
编译和执行上面的代码时,它产生的结果如下 -
Value of var variable: 20
Address stored in ip variable: 0xbfc601ac
Value of *ip variable: 20
C ++中的指针
指针有很多但很简单的概念,它们对C ++编程非常重要。 C ++程序员应该清楚以下几个重要的指针概念 -
Sr.No | 概念与描述 |
---|---|
1 | 空指针 C ++支持空指针,这是一个在几个标准库中定义的值为零的常量。 |
2 | 指针算术 可以在指针上使用四个算术运算符:++, - ,+, - |
3 | 指针与数组 指针和数组之间存在密切关系。 |
4 | 指针数组 您可以定义数组以包含许多指针。 |
5 | 指针指针 C ++允许您在指针上指针等等。 |
6 | 将指针传递给函数 通过引用或地址传递参数都可以通过被调用函数在调用函数中更改传递的参数。 |
7 | 从函数返回指针 C ++允许函数返回指向局部变量,静态变量和动态分配内存的指针。 |
C++ References
引用变量是别名,即现有变量的另一个名称。 使用变量初始化引用后,可以使用变量名称或引用名称来引用变量。
参考文献与指针
引用经常与指针混淆,但引用和指针之间的三个主要区别是 -
您不能有NULL引用。 您必须始终能够假定引用连接到合法的存储区。
将引用初始化为对象后,无法将其更改为引用另一个对象。 指针可以随时指向另一个对象。
必须在创建引用时初始化引用。 指针可以随时初始化。
用C ++创建引用
将变量名称视为附加到变量在内存中的位置的标签。 然后,您可以将引用视为附加到该内存位置的第二个标签。 因此,您可以通过原始变量名称或引用来访问变量的内容。 例如,假设我们有以下示例 -
int i = 17;
我们可以为i声明引用变量,如下所示。
int& r = i;
阅读&在这些声明中作为reference 。 因此,将第一个声明读作“r是初始化为i的整数引用”,并将第二个声明读作“s是初始化为d的双引用”。 以下示例使用int和double的引用 -
#include <iostream>
using namespace std;
int main () {
// declare simple variables
int i;
double d;
// declare reference variables
int& r = i;
double& s = d;
i = 5;
cout << "Value of i : " << i << endl;
cout << "Value of i reference : " << r << endl;
d = 11.7;
cout << "Value of d : " << d << endl;
cout << "Value of d reference : " << s << endl;
return 0;
}
当上面的代码一起编译并执行时,它会产生以下结果 -
Value of i : 5
Value of i reference : 5
Value of d : 11.7
Value of d reference : 11.7
引用通常用于函数参数列表和函数返回值。 以下是与C ++引用相关的两个重要主题,对于C ++程序员来说应该是清楚的 -
Sr.No | 概念与描述 |
---|---|
1 | 参考作为参数 C ++支持比参数更安全地将引用作为函数参数传递。 |
2 | 参考为返回值 您可以像任何其他数据类型一样从C ++函数返回引用。 |
C++ Date and Time
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 ++标准库来检查它们的详细信息。
Sr.No | 功能与目的 |
---|---|
1 | time_t time(time_t *time); 这将返回自1970年1月1日以来经过的秒数内系统的当前日历时间。如果系统没有时间,则返回.1。 |
2 | char *ctime(const time_t *time); 这将返回指向表格day month year hours:minutes:seconds year\n\0的字符串的指针day month year hours:minutes:seconds year\n\0 。 |
3 | struct tm *localtime(const time_t *time); 这将返回指向表示本地时间的tm结构的指针。 |
4 | clock_t clock(void); 这将返回一个值,该值近似于调用程序运行的时间。 如果时间不可用,则返回值.1。 |
5 | char * asctime ( const struct tm * time ); 这将返回一个指向字符串的指针,该字符串包含存储在转换为表单的时间所指向的结构中的信息:day month date hours:minutes:seconds year\n\0 |
6 | struct tm *gmtime(const time_t *time); 这将以tm结构的形式返回指向时间的指针。 时间以协调世界时(UTC)表示,其基本上是格林威治标准时间(GMT)。 |
7 | time_t mktime(struct tm *time); 这将返回与time指向的结构中找到的时间等效的日历时间。 |
8 | double difftime ( time_t time2, time_t time1 ); 此函数计算time1和time2之间的秒数差异。 |
9 | size_t strftime(); 此功能可用于以特定格式格式化日期和时间。 |
当前日期和时间
假设您要检索当前系统日期和时间,可以是本地时间,也可以是协调世界时(UTC)。 以下是实现相同的例子 -
#include <iostream>
#include <ctime>
using namespace std;
int main() {
// current date/time based on current system
time_t now = time(0);
// convert now to string form
char* dt = ctime(&now);
cout << "The local date and time is: " << dt << endl;
// convert now to tm struct for UTC
tm *gmtm = gmtime(&now);
dt = asctime(gmtm);
cout << "The UTC date and time is:"<< dt << endl;
}
编译并执行上述代码时,会产生以下结果 -
The local date and time is: Sat Jan 8 20:07:41 2011
The UTC date and time is:Sun Jan 9 03:07:41 2011
使用struct tm格式化时间
在C或C ++中使用日期和时间时, tm结构非常重要。 该结构以如上所述的C结构的形式保持日期和时间。 大多数时候相关的函数都使用了tm结构。 以下是一个利用各种日期和时间相关函数和tm结构的例子 -
在本章中使用结构时,我假设您对C结构以及如何使用arrow - >运算符访问结构成员有基本的了解。
#include <iostream>
#include <ctime>
using namespace std;
int main() {
// current date/time based on current system
time_t now = time(0);
cout << "Number of sec since January 1,1970:" << now << endl;
tm *ltm = localtime(&now);
// print various components of tm structure.
cout << "Year" << 1970 + ltm->tm_year<<endl;
cout << "Month: "<< 1 + ltm->tm_mon<< endl;
cout << "Day: "<< ltm->tm_mday << endl;
cout << "Time: "<< 1 + ltm->tm_hour << ":";
cout << 1 + ltm->tm_min << ":";
cout << 1 + ltm->tm_sec << endl;
}
编译并执行上述代码时,会产生以下结果 -
Number of sec since January 1, 1970:1294548238
Year: 2011
Month: 1
Day: 8
Time: 22: 44:59
C++ Basic Input/Output
C ++标准库提供了一组广泛的输入/输出功能,我们将在后续章节中看到。 本章将讨论C ++编程所需的非常基本和最常见的I/O操作。
C ++ I/O发生在流中,它是字节序列。 如果字节从键盘,磁盘驱动器或网络连接等设备流向主存储器,则称为input operation ,如果字节从主存储器流向显示屏,打印机,磁盘驱动器等设备或者网络连接等,这称为output operation 。
I/O库头文件
以下头文件对C ++程序很重要 -
Sr.No | 头文件和功能及描述 |
---|---|
1 | 《iostream》 此文件定义cin, cout, cerr和clog对象,它们分别对应于标准输入流,标准输出流,未缓冲的标准错误流和缓冲的标准错误流。 |
2 | 《iomanip》 此文件声明了对使用所谓的参数化流操作setprecision (例如setw和setprecision执行格式化I/O有用的服务。 |
3 | 《fstream》 此文件声明用于用户控制的文件处理的服务。 我们将在文件和流相关章节中详细讨论它。 |
标准输出流(cout)
预定义对象cout是ostream类的实例。 cout对象被称为“连接”标准输出设备,通常是显示屏。 cout与流插入操作符一起使用,该操作符被写为“”,它比符号小两个,如下例所示。
#include <iostream>
using namespace std;
int main() {
char str[] = "Hello C++";
cout << "Value of str is : " << str << endl;
}
编译并执行上述代码时,会产生以下结果 -
Value of str is : Hello C++
C ++编译器还确定要输出的变量的数据类型,并选择适当的流插入运算符来显示该值。 <
插入运算符<endl用于在行的末尾添加换行符。
标准输入流(cin)
预定义对象cin是istream类的实例。 据说cin对象连接到标准输入设备,通常是键盘。 cin与流提取操作符一起使用,该操作符被写为“”,它比符号大两个,如下例所示。
#include <iostream>
using namespace std;
int main() {
char name[50];
cout << "Please enter your name: ";
cin >> name;
cout << "Your name is: " << name << endl;
}
编译并执行上述代码时,将提示您输入名称。 输入一个值,然后按Enter键以查看以下结果 -
Please enter your name: cplusplus
Your name is: cplusplus
C ++编译器还确定输入值的数据类型,并选择适当的流提取运算符以提取值并将其存储在给定变量中。
流提取运算符>>可以在单个语句中使用多次。 要请求多个数据,您可以使用以下内容 -
cin >> name >> age;
这相当于以下两个陈述 -
cin >> name;
cin >> age;
标准错误流(cerr)
预定义对象cerr是ostream类的实例。 cerr对象被称为附加到标准错误设备,该设备也是显示屏,但是对象cerr是非缓冲的,并且每个流插入到cerr会导致其输出立即出现。
cerr还与流插入运算符一起使用,如以下示例所示。
#include <iostream>
using namespace std;
int main() {
char str[] = "Unable to read....";
cerr << "Error message : " << str << endl;
}
编译并执行上述代码时,会产生以下结果 -
Error message : Unable to read....
标准日志流(clog)
预定义对象clog是ostream类的实例。 据说clog对象附加到标准错误设备,标准错误设备也是显示屏,但缓冲了对象clog 。 这意味着每次插入阻塞都可能导致其输出保持在缓冲区中,直到填充缓冲区或刷新缓冲区为止。
clog还与流插入运算符一起使用,如以下示例所示。
#include <iostream>
using namespace std;
int main() {
char str[] = "Unable to read....";
clog << "Error message : " << str << endl;
}
编译并执行上述代码时,会产生以下结果 -
Error message : Unable to read....
你可能无法看到cout,cerr和clog与这些小例子的任何区别,但在编写和执行大程序时,差异变得明显。 因此,最好使用cerr流显示错误消息,并在显示其他日志消息时应使用clog。
C++ Data Structures
C/C ++数组允许您定义组合相同类型的多个数据项的变量,但structure是另一个用户定义的数据类型,它允许您组合不同类型的数据项。
结构用于表示记录,假设您想要在库中跟踪您的书籍。 您可能希望跟踪每本书的以下属性 -
- Title
- Author
- Subject
- 书名
定义一个结构 (Defining a Structure)
要定义结构,必须使用struct语句。 struct语句为您的程序定义了一个具有多个成员的新数据类型。 struct语句的格式是这样的 -
struct [structure tag] {
member definition;
member definition;
...
member definition;
} [one or more structure variables];
structure tag是可选的,每个成员定义是一个普通的变量定义,例如int i; 或浮动f; 或任何其他有效的变量定义。 在结构定义的最后,在最后一个分号之前,您可以指定一个或多个结构变量,但它是可选的。 以下是您声明Book结构的方式 -
struct Books {
char title[50];
char author[50];
char subject[100];
int book_id;
} book;
访问结构成员 (Accessing Structure Members)
要访问结构的任何成员,我们使用member access operator (.) 。 成员访问运算符被编码为结构变量名称和我们希望访问的结构成员之间的句点。 您可以使用struct关键字来定义结构类型的变量。 以下是解释结构用法的示例 -
#include <iostream>
#include <cstring>
using namespace std;
struct Books {
char title[50];
char author[50];
char subject[100];
int book_id;
};
int main() {
struct Books Book1; // Declare Book1 of type Book
struct Books Book2; // Declare Book2 of type Book
// book 1 specification
strcpy( Book1.title, "Learn C++ Programming");
strcpy( Book1.author, "Chand Miyan");
strcpy( Book1.subject, "C++ Programming");
Book1.book_id = 6495407;
// book 2 specification
strcpy( Book2.title, "Telecom Billing");
strcpy( Book2.author, "Yakit Singha");
strcpy( Book2.subject, "Telecom");
Book2.book_id = 6495700;
// Print Book1 info
cout << "Book 1 title : " << Book1.title <<endl;
cout << "Book 1 author : " << Book1.author <<endl;
cout << "Book 1 subject : " << Book1.subject <<endl;
cout << "Book 1 id : " << Book1.book_id <<endl;
// Print Book2 info
cout << "Book 2 title : " << Book2.title <<endl;
cout << "Book 2 author : " << Book2.author <<endl;
cout << "Book 2 subject : " << Book2.subject <<endl;
cout << "Book 2 id : " << Book2.book_id <<endl;
return 0;
}
编译并执行上述代码时,会产生以下结果 -
Book 1 title : Learn C++ Programming
Book 1 author : Chand Miyan
Book 1 subject : C++ Programming
Book 1 id : 6495407
Book 2 title : Telecom Billing
Book 2 author : Yakit Singha
Book 2 subject : Telecom
Book 2 id : 6495700
作为函数参数的结构 (Structures as Function Arguments)
您可以将结构作为函数参数传递,与传递任何其他变量或指针的方式非常相似。 您将以与上面示例中访问过的方式类似的方式访问结构变量 -
#include <iostream>
#include <cstring>
using namespace std;
void printBook( struct Books book );
struct Books {
char title[50];
char author[50];
char subject[100];
int book_id;
};
int main() {
struct Books Book1; // Declare Book1 of type Book
struct Books Book2; // Declare Book2 of type Book
// book 1 specification
strcpy( Book1.title, "Learn C++ Programming");
strcpy( Book1.author, "Chand Miyan");
strcpy( Book1.subject, "C++ Programming");
Book1.book_id = 6495407;
// book 2 specification
strcpy( Book2.title, "Telecom Billing");
strcpy( Book2.author, "Yakit Singha");
strcpy( Book2.subject, "Telecom");
Book2.book_id = 6495700;
// Print Book1 info
printBook( Book1 );
// Print Book2 info
printBook( Book2 );
return 0;
}
void printBook( struct Books book ) {
cout << "Book title : " << book.title <<endl;
cout << "Book author : " << book.author <<endl;
cout << "Book subject : " << book.subject <<endl;
cout << "Book id : " << book.book_id <<endl;
}
编译并执行上述代码时,会产生以下结果 -
Book title : Learn C++ Programming
Book author : Chand Miyan
Book subject : C++ Programming
Book id : 6495407
Book title : Telecom Billing
Book author : Yakit Singha
Book subject : Telecom
Book id : 6495700
指向结构的指针
您可以按照与定义指向任何其他变量的指针非常相似的方式定义指向结构的指针,如下所示 -
struct Books *struct_pointer;
现在,您可以将结构变量的地址存储在上面定义的指针变量中。 要查找结构变量的地址,请将&运算符放在结构名称之前,如下所示 -
struct_pointer = &Book1;
要使用指向该结构的指针访问结构的成员,必须使用 - >运算符,如下所示 -
struct_pointer->title;
让我们用结构指针重新编写上面的例子,希望这对你来说很容易理解 -
#include <iostream>
#include <cstring>
using namespace std;
void printBook( struct Books *book );
struct Books {
char title[50];
char author[50];
char subject[100];
int book_id;
};
int main() {
struct Books Book1; // Declare Book1 of type Book
struct Books Book2; // Declare Book2 of type Book
// Book 1 specification
strcpy( Book1.title, "Learn C++ Programming");
strcpy( Book1.author, "Chand Miyan");
strcpy( Book1.subject, "C++ Programming");
Book1.book_id = 6495407;
// Book 2 specification
strcpy( Book2.title, "Telecom Billing");
strcpy( Book2.author, "Yakit Singha");
strcpy( Book2.subject, "Telecom");
Book2.book_id = 6495700;
// Print Book1 info, passing address of structure
printBook( &Book1 );
// Print Book1 info, passing address of structure
printBook( &Book2 );
return 0;
}
// This function accept pointer to structure as parameter.
void printBook( struct Books *book ) {
cout << "Book title : " << book->title <<endl;
cout << "Book author : " << book->author <<endl;
cout << "Book subject : " << book->subject <<endl;
cout << "Book id : " << book->book_id <<endl;
}
编译并执行上述代码时,会产生以下结果 -
Book title : Learn C++ Programming
Book author : Chand Miyan
Book subject : C++ Programming
Book id : 6495407
Book title : Telecom Billing
Book author : Yakit Singha
Book subject : Telecom
Book id : 6495700
typedef关键字
有一种更简单的方法来定义结构,或者您可以“创建”别名类型。 例如 -
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都是指向long int的指针。
C++ Classes and Objects
C ++编程的主要目的是为C编程语言添加面向对象,类是C ++的核心特性,支持面向对象的编程,通常称为用户定义类型。
类用于指定对象的形式,它将数据表示和方法组合在一起,以便将数据操作到一个整齐的包中。 类中的数据和函数称为类的成员。
C++ Class Definitions
定义类时,可以为数据类型定义蓝图。 这实际上并没有定义任何数据,但它确实定义了类名的含义,即类的对象将包含什么以及可以对这样的对象执行什么操作。
类定义以关键字class开头,后跟类名; 和一个由一对花括号括起来的类体。 类定义必须遵循分号或声明列表。 例如,我们使用关键字class定义Box数据类型,如下所示 -
class Box {
public:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
关键字public确定其后面的类成员的访问属性。 可以在类对象范围内的任何位置从类外部访问公共成员。 您还可以将类的成员指定为private或protected ,我们将在子节中讨论。
定义C ++对象
类提供对象的蓝图,因此基本上是从类创建对象。 我们声明一个类的对象与我们声明基本类型变量的声明完全相同。 以下语句声明了Box类的两个对象 -
Box Box1; // Declare Box1 of type Box
Box Box2; // Declare Box2 of type Box
Box1和Box2这两个对象都有自己的数据成员副本。
访问数据成员
可以使用直接成员访问运算符(。)访问类对象的公共数据成员。 让我们尝试下面的例子来说清楚事情 -
#include <iostream>
using namespace std;
class Box {
public:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
int main() {
Box Box1; // Declare Box1 of type Box
Box Box2; // Declare Box2 of type Box
double volume = 0.0; // Store the volume of a box here
// box 1 specification
Box1.height = 5.0;
Box1.length = 6.0;
Box1.breadth = 7.0;
// box 2 specification
Box2.height = 10.0;
Box2.length = 12.0;
Box2.breadth = 13.0;
// volume of box 1
volume = Box1.height * Box1.length * Box1.breadth;
cout << "Volume of Box1 : " << volume <<endl;
// volume of box 2
volume = Box2.height * Box2.length * Box2.breadth;
cout << "Volume of Box2 : " << volume <<endl;
return 0;
}
编译并执行上述代码时,会产生以下结果 -
Volume of Box1 : 210
Volume of Box2 : 1560
请务必注意,无法使用直接成员访问运算符(。)直接访问私有成员和受保护成员。 我们将了解如何访问私有和受保护的成员。
详细的类和对象
到目前为止,您已经对C ++类和对象有了非常基本的了解。 还有一些与C ++类和对象相关的有趣概念,我们将在下面列出的各个子部分中讨论这些概念 -
Sr.No | 概念与描述 |
---|---|
1 | class成员职能 类的成员函数是一个函数,它在类定义中的定义或原型与任何其他变量一样。 |
2 | 类访问修饰符 类成员可以定义为public,private或protected。 默认情况下,成员将被视为私有。 |
3 | 构造函数和析构函数 类构造函数是类中的特殊函数,在创建类的新对象时调用该类。 析构函数也是一个特殊函数,在删除创建的对象时调用。 |
4 | 复制构造函数 复制构造函数是一个构造函数,它通过使用先前创建的同一类的对象初始化它来创建对象。 |
5 | 朋友的功能 允许friend函数完全访问类的私有和受保护成员。 |
6 | 内联函数 使用内联函数,编译器会尝试扩展函数体中的代码,而不是调用函数。 |
7 | 这个指针 每个对象都有一个特殊的指针,指向对象本身。 |
8 | 指向C ++类的指针 指向类的指针与指向结构的指针完全相同。 实际上,一个类实际上只是一个包含函数的结构。 |
9 | 静态成员 类的数据成员和函数成员都可以声明为static。 |
C++ Inheritance
面向对象编程中最重要的概念之一是继承。 继承允许我们根据另一个类定义一个类,这使得创建和维护应用程序变得更容易。 这也提供了重用代码功能和快速实现时间的机会。
在创建类时,程序员可以指定新类应该继承现有类的成员,而不是编写全新的数据成员和成员函数。 此现有类称为base类,新类称为derived类。
继承的想法实现了is a关系。 例如,哺乳动物IS-A动物,狗IS-A哺乳动物因此也是狗IS-A动物等等。
基础和派生类
一个类可以从多个类派生,这意味着它可以从多个基类继承数据和函数。 为了定义派生类,我们使用类派生列表来指定基类。 类派生列表命名一个或多个基类,并具有以下形式 -
class derived-class: access-specifier base-class
其中access-specifier是public, protected,或private ,而base-class是先前定义的类的名称。 如果未使用访问说明符,则默认情况下它是私有的。
考虑一个基类Shape及其派生类Rectangle ,如下所示 -
#include <iostream>
using namespace std;
// Base class
class Shape {
public:
void setWidth(int w) {
width = w;
}
void setHeight(int h) {
height = h;
}
protected:
int width;
int height;
};
// Derived class
class Rectangle: public Shape {
public:
int getArea() {
return (width * height);
}
};
int main(void) {
Rectangle Rect;
Rect.setWidth(5);
Rect.setHeight(7);
// Print the area of the object.
cout << "Total area: " << Rect.getArea() << endl;
return 0;
}
编译并执行上述代码时,会产生以下结果 -
Total area: 35
访问控制和继承
派生类可以访问其基类的所有非私有成员。 因此,派生类的成员函数不应该可以访问的基类成员应该在基类中声明为private。
我们可以根据以下方式汇总不同的访问类型 - 谁可以通过以下方式访问它们 -
访问 | 上市 | 保护 | 私人的 |
---|---|---|---|
Same class | yes | yes | yes |
派生类 | yes | yes | no |
Outside classes | yes | no | no |
派生类继承所有基类方法,但以下情况除外 -
- 基类的构造函数,析构函数和复制构造函数。
- 重载基类的运算符。
- 基类的朋友功能。
继承类型
从基类派生类时,可以通过public, protected或private继承继承基类。 继承类型由access-specifier指定,如上所述。
我们几乎不使用protected或private继承,但通常使用public继承。 使用不同类型的继承时,应用以下规则 -
Public Inheritance - 从public基类派生类时,基类的public成员成为派生类的public成员,基类的protected成员成为派生类的protected成员。 基类的private成员永远不能直接从派生类访问,但可以通过调用基类的public成员和protected成员来访问。
Protected Inheritance - 从protected基类派生时,基类的public成员和protected成员将成为派生类的protected成员。
Private Inheritance - 从private基类派生时,基类的public成员和protected成员将成为派生类的private成员。
多重继承
C ++类可以从多个类继承成员,这里是扩展语法 -
class derived-class: access baseA, access baseB....
访问权限是public, protected,或private并且将为每个基类提供,并且它们将用逗号分隔,如上所示。 让我们尝试以下示例 -
#include <iostream>
using namespace std;
// Base class Shape
class Shape {
public:
void setWidth(int w) {
width = w;
}
void setHeight(int h) {
height = h;
}
protected:
int width;
int height;
};
// Base class PaintCost
class PaintCost {
public:
int getCost(int area) {
return area * 70;
}
};
// Derived class
class Rectangle: public Shape, public PaintCost {
public:
int getArea() {
return (width * height);
}
};
int main(void) {
Rectangle Rect;
int area;
Rect.setWidth(5);
Rect.setHeight(7);
area = Rect.getArea();
// Print the area of the object.
cout << "Total area: " << Rect.getArea() << endl;
// Print the total cost of painting
cout << "Total paint cost: $" << Rect.getCost(area) << endl;
return 0;
}
编译并执行上述代码时,会产生以下结果 -
Total area: 35
Total paint cost: $2450
C++ Overloading (Operator and Function)
C ++允许您为同一范围内的function名称或operator指定多个定义,分别称为function overloading和operator overloading 。
重载声明是声明,声明与同一作用域中先前声明的声明具有相同的名称,除了两个声明具有不同的参数和明显不同的定义(实现)。
当您调用重载function或operator ,编译器通过将用于调用函数或运算符的参数类型与定义中指定的参数类型进行比较,确定要使用的最合适的定义。 选择最合适的重载函数或运算符的过程称为overload resolution 。
C ++中的函数重载
您可以在同一范围内对同一函数名称具有多个定义。 函数的定义必须通过参数列表中的参数的类型和/或数量彼此不同。 您不能重载仅由返回类型不同的函数声明。
以下是使用相同函数print()打印不同数据类型的示例 -
#include <iostream>
using namespace std;
class printData {
public:
void print(int i) {
cout << "Printing int: " << i << endl;
}
void print(double f) {
cout << "Printing float: " << f << endl;
}
void print(char* c) {
cout << "Printing character: " << c << endl;
}
};
int main(void) {
printData pd;
// Call print to print integer
pd.print(5);
// Call print to print float
pd.print(500.263);
// Call print to print character
pd.print("Hello C++");
return 0;
}
编译并执行上述代码时,会产生以下结果 -
Printing int: 5
Printing float: 500.263
Printing character: Hello C++
运算符在C ++中重载
您可以重新定义或重载C ++中可用的大多数内置运算符。 因此,程序员也可以使用具有用户定义类型的运算符。
重载运算符是具有特殊名称的函数:关键字“operator”后跟要定义的运算符的符号。 与任何其他函数一样,重载运算符具有返回类型和参数列表。
Box operator+(const Box&);
声明可用于add两个Box对象并返回最终Box对象的加法运算符。 大多数重载运算符可以定义为普通的非成员函数或类成员函数。 如果我们将上面的函数定义为类的非成员函数,那么我们必须为每个操作数传递两个参数,如下所示 -
Box operator+(const Box&, const Box&);
以下是使用成员函数显示运算符过载概念的示例。 这里一个对象作为参数传递,其属性将使用该对象访问,可以使用this运算符访问将调用此运算符的对象,如下所述 -
#include <iostream>
using namespace std;
class Box {
public:
double getVolume(void) {
return length * breadth * height;
}
void setLength( double len ) {
length = len;
}
void setBreadth( double bre ) {
breadth = bre;
}
void setHeight( double hei ) {
height = hei;
}
// Overload + operator to add two Box objects.
Box operator+(const Box& b) {
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
// Main function for the program
int main() {
Box Box1; // Declare Box1 of type Box
Box Box2; // Declare Box2 of type Box
Box Box3; // Declare Box3 of type Box
double volume = 0.0; // Store the volume of a box here
// box 1 specification
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// box 2 specification
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// volume of box 1
volume = Box1.getVolume();
cout << "Volume of Box1 : " << volume <<endl;
// volume of box 2
volume = Box2.getVolume();
cout << "Volume of Box2 : " << volume <<endl;
// Add two object as follows:
Box3 = Box1 + Box2;
// volume of box 3
volume = Box3.getVolume();
cout << "Volume of Box3 : " << volume <<endl;
return 0;
}
编译并执行上述代码时,会产生以下结果 -
Volume of Box1 : 210
Volume of Box2 : 1560
Volume of Box3 : 5400
Overloadable/Non-overloadableOperators
以下是可以重载的运算符列表 -
+ | - | * | / | % | ^ |
& | | | ~ | ! | , | = |
< | > | <= | >= | ++ | -- |
<< | >> | == | != | && | || |
+= | -= | /= | %= | ^= | &= |
|= | *= | <<= | >>= | [] | () |
-> | ->* | new | new [] | delete | delete [] |
以下是无法重载的运算符列表 -
:: | .* | . | ?: |
运算符重载示例
以下是各种运算符重载示例,可帮助您理解概念。
Sr.No | 运算符和示例 |
---|---|
1 | 一元运算符重载 |
2 | 二进制运算符重载 |
3 | 关系运算符重载 |
4 | 输入/输出运算符重载 |
5 | ++和 - 运算符重载 |
6 | 赋值运算符重载 |
7 | 函数call()运算符重载 |
8 | Subscripting [] Operator Overloading |
9 | 类成员访问运算符 - >重载 |
Polymorphism in C++
polymorphism这个词意味着有许多形式。 通常,当存在类的层次结构并且它们通过继承相关时,会发生多态性。
C ++多态意味着对成员函数的调用将导致执行不同的函数,具体取决于调用该函数的对象的类型。
请考虑以下示例,其中基类已由其他两个类派生 -
#include <iostream>
using namespace std;
class Shape {
protected:
int width, height;
public:
Shape( int a = 0, int b = 0){
width = a;
height = b;
}
int area() {
cout << "Parent class area :" <<endl;
return 0;
}
};
class Rectangle: public Shape {
public:
Rectangle( int a = 0, int b = 0):Shape(a, b) { }
int area () {
cout << "Rectangle class area :" <<endl;
return (width * height);
}
};
class Triangle: public Shape {
public:
Triangle( int a = 0, int b = 0):Shape(a, b) { }
int area () {
cout << "Triangle class area :" <<endl;
return (width * height/2);
}
};
// Main function for the program
int main() {
Shape *shape;
Rectangle rec(10,7);
Triangle tri(10,5);
// store the address of Rectangle
shape = &rec;
// call rectangle area.
shape->area();
// store the address of Triangle
shape = &tri;
// call triangle area.
shape->area();
return 0;
}
编译并执行上述代码时,会产生以下结果 -
Parent class area :
Parent class area :
输出错误的原因是函数area()的调用由编译器设置为基类中定义的版本。 这称为函数调用的static resolution ,或static linkage - 在执行程序之前修复函数调用。 这有时也称为early binding因为在编译程序期间设置了area()函数。
但是现在,让我们在程序中进行一些修改,然后在Shape类中使用关键字virtual声明area(),使其看起来像这样 -
class Shape {
protected:
int width, height;
public:
Shape( int a = 0, int b = 0) {
width = a;
height = b;
}
virtual int area() {
cout << "Parent class area :" <<endl;
return 0;
}
};
在稍作修改之后,当编译并执行前面的示例代码时,它会产生以下结果 -
Rectangle class area
Triangle class area
这次,编译器查看指针的内容而不是它的类型。 因此,由于tri和rec类的对象的地址以* shape存储,因此调用相应的area()函数。
如您所见,每个子类都有一个单独的函数区域实现()。 这就是通常使用polymorphism方式。 您有不同的类具有相同名称的功能,甚至相同的参数,但具有不同的实现。
虚函数
virtual函数是使用关键字virtual声明的基类中的函数。 在基类中定义一个虚函数,在派生类中使用另一个版本,向编译器发出信号,告知我们不希望此函数有静态链接。
我们想要的是在程序中的任何给定点选择要调用的函数,使其基于调用它的对象的类型。 这种操作称为dynamic linkage或late binding 。
纯虚函数 (Pure Virtual Functions)
您可能希望在基类中包含虚函数,以便可以在派生类中重新定义它以适合该类的对象,但是您可以为基类中的函数提供有意义的定义。
我们可以将基类中的虚函数区域()更改为以下内容 -
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告诉编译器该函数没有主体,上面的虚函数将被称为pure virtual function 。
Data Abstraction in C++
数据抽象是指仅向外界提供基本信息并隐藏其背景细节,即在不显示细节的情况下在程序中表示所需信息。
数据抽象是一种编程(和设计)技术,它依赖于接口和实现的分离。
让我们来看一个电视的真实例子,你可以打开和关闭,更改频道,调整音量,添加外部组件,如扬声器,录像机和DVD播放器,但你不知道它的内部细节,是的,你不知道它是如何通过空中或通过电缆接收信号,如何转换信号,最后将它们显示在屏幕上。
因此,我们可以说电视清楚地将其内部实现与其外部接口分开,您可以使用其电源按钮,频道切换器和音量控制等接口,而不必了解其内部结构。
在C ++中,类提供了很高级别的data abstraction 。 它们为外界提供了足够的公共方法来使用对象的功能并操纵对象数据,即状态而不实际知道内部如何实现类。
例如,您的程序可以调用sort()函数,而无需知道函数实际用于对给定值进行排序的算法。 实际上,排序功能的底层实现可能会在库的版本之间发生变化,只要界面保持不变,您的函数调用仍然可以正常工作。
在C ++中,我们使用classes来定义自己的抽象数据类型(ADT)。 您可以使用类ostream的cout对象将数据流式传输到标准输出,如下所示 -
#include <iostream>
using namespace std;
int main() {
cout << "Hello C++" <<endl;
return 0;
}
在这里,您无需了解cout如何在用户屏幕上显示文本。 您只需要知道公共接口,'cout'的底层实现可以自由更改。
访问标签实施抽象
在C ++中,我们使用访问标签来定义类的抽象接口。 一个类可能包含零个或多个访问标签 -
使用公共标签定义的成员可以访问该计划的所有部分。 类型的数据抽象视图由其公共成员定义。
使用该类的代码无法访问使用私有标签定义的成员。 私有部分隐藏了使用该类型的代码的实现。
访问标签的出现频率没有限制。 每个访问标签指定后续成员定义的访问级别。 在遇到下一个访问标签或看到类主体的右侧右括号之前,指定的访问级别仍然有效。
数据抽象的好处
数据抽象提供了两个重要的优势 -
保护类内部不受无意的用户级错误的影响,这可能会破坏对象的状态。
类实现可能会随着时间的推移而发生变化,以响应不断变化的需求或错误报告,而无需更改用户级代码。
通过仅在类的私有部分中定义数据成员,类作者可以自由地对数据进行更改。 如果实现发生更改,则只需要检查类代码以查看更改可能产生的影响。 如果数据是公共的,那么任何直接访问旧表示的数据成员的函数都可能被破坏。
数据抽象示例
任何使用公共和私有成员实现类的C ++程序都是数据抽象的一个例子。 考虑以下示例 -
#include <iostream>
using namespace std;
class Adder {
public:
// constructor
Adder(int i = 0) {
total = i;
}
// interface to outside world
void addNum(int number) {
total += number;
}
// interface to outside world
int getTotal() {
return total;
};
private:
// hidden data from outside world
int total;
};
int main() {
Adder a;
a.addNum(10);
a.addNum(20);
a.addNum(30);
cout << "Total " << a.getTotal() <<endl;
return 0;
}
编译并执行上述代码时,会产生以下结果 -
Total 60
上面的类将数字加在一起,并返回总和。 公共成员 - addNum和getTotal是外部世界的接口,用户需要知道它们才能使用该类。 私有成员total是用户不需要了解的内容,但是该类需要正常运行。
设计策略
抽象将代码分为接口和实现。 因此,在设计组件时,必须保持接口独立于实现,以便在更改底层实现时,接口将保持不变。
在这种情况下,无论程序使用这些接口,它们都不会受到影响,只需要使用最新的实现进行重新编译。
Data Encapsulation in C++
所有C ++程序都由以下两个基本元素组成 -
Program statements (code) - 这是执行操作的程序的一部分,它们被称为函数。
Program data - 数据是受程序功能影响的程序信息。
封装是一种面向对象的编程概念,它将操作数据的数据和功能绑定在一起,并保护其免受外部干扰和误用。 数据封装导致了重要的OOP data hiding概念。
Data encapsulation是捆绑数据的机制,使用它们和data abstraction是仅暴露接口并从用户隐藏实现细节的机制。
C ++通过创建用户定义类型(称为classes支持封装和数据隐藏的属性。 我们已经研究过一个类可以包含private, protected和public成员。 默认情况下,类中定义的所有项都是私有的。 例如 -
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
};
变量length,widthth和height是private 。 这意味着它们只能由Box类的其他成员访问,而不能由程序的任何其他部分访问。 这是实现封装的一种方式。
要使类的某些部分成为public类(即,可以访问程序的其他部分),必须在public关键字之后声明它们。 公共说明符后定义的所有变量或函数都可由程序中的所有其他函数访问。
使一个类成为另一个类的朋友会暴露实现细节并减少封装。 理想的是保持每个类的许多细节尽可能地隐藏在所有其他类中。
数据封装示例
任何使用公共和私有成员实现类的C ++程序都是数据封装和数据抽象的一个例子。 考虑以下示例 -
#include <iostream>
using namespace std;
class Adder {
public:
// constructor
Adder(int i = 0) {
total = i;
}
// interface to outside world
void addNum(int number) {
total += number;
}
// interface to outside world
int getTotal() {
return total;
};
private:
// hidden data from outside world
int total;
};
int main() {
Adder a;
a.addNum(10);
a.addNum(20);
a.addNum(30);
cout << "Total " << a.getTotal() <<endl;
return 0;
}
编译并执行上述代码时,会产生以下结果 -
Total 60
上面的类将数字加在一起,并返回总和。 公共成员addNum和getTotal是外部世界的接口,用户需要知道它们才能使用该类。 私有成员total是对外界隐藏的东西,但是课程需要正常运作。
设计策略
除非我们真的需要公开它们,否则我们大多数人都学会了默认将类成员设为私有。 这只是很好的encapsulation 。
这最常应用于数据成员,但它同样适用于所有成员,包括虚拟功能。
Interfaces in C++ (Abstract Classes)
接口描述C ++类的行为或功能,而不提交该类的特定实现。
C ++接口使用abstract classes实现,这些抽象类不应与数据抽象混淆,数据抽象是将实现细节与关联数据分开的概念。
通过将其至少一个函数声明为pure virtual函数,使类成为抽象类。 通过在声明中放置“= 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
};
abstract class (通常称为ABC)的目的是提供适当的基类,其他类可以从中继承。 抽象类不能用于实例化对象,仅用作interface 。 尝试实例化抽象类的对象会导致编译错误。
因此,如果需要实例化ABC的子类,则必须实现每个虚函数,这意味着它支持ABC声明的接口。 未能覆盖派生类中的纯虚函数,然后尝试实例化该类的对象,是编译错误。
可用于实例化对象的concrete classes称为concrete classes 。
抽象类示例
考虑以下示例,其中父类提供基类的接口以实现名为getArea()的函数 -
#include <iostream>
using namespace std;
// Base class
class Shape {
public:
// pure virtual function providing interface framework.
virtual int getArea() = 0;
void setWidth(int w) {
width = w;
}
void setHeight(int h) {
height = h;
}
protected:
int width;
int height;
};
// Derived classes
class Rectangle: public Shape {
public:
int getArea() {
return (width * height);
}
};
class Triangle: public Shape {
public:
int getArea() {
return (width * height)/2;
}
};
int main(void) {
Rectangle Rect;
Triangle Tri;
Rect.setWidth(5);
Rect.setHeight(7);
// Print the area of the object.
cout << "Total Rectangle area: " << Rect.getArea() << endl;
Tri.setWidth(5);
Tri.setHeight(7);
// Print the area of the object.
cout << "Total Triangle area: " << Tri.getArea() << endl;
return 0;
}
编译并执行上述代码时,会产生以下结果 -
Total Rectangle area: 35
Total Triangle area: 17
您可以看到抽象类如何根据getArea()定义接口,另外两个类实现相同的函数但使用不同的算法来计算特定于形状的区域。
设计策略
面向对象的系统可能使用抽象基类来提供适用于所有外部应用程序的通用标准化接口。 然后,通过从该抽象基类的继承,形成类似操作的派生类。
外部应用程序提供的功能(即公共功能)在抽象基类中作为纯虚函数提供。 这些纯虚函数的实现在派生类中提供,这些派生类对应于应用程序的特定类型。
即使在定义了系统之后,该体系结构也允许将新应用程序轻松添加到系统中。
C++ Files and Streams
到目前为止,我们一直在使用iostream标准库,它提供了cin和cout方法,分别用于读取标准输入和写入标准输出。
本教程将教您如何从文件中读取和写入。 这需要另一个名为fstream标准C ++库,它定义了三种新的数据类型 -
Sr.No | 数据类型和描述 |
---|---|
1 | ofstream 此数据类型表示输出文件流,用于创建文件和将信息写入文件。 |
2 | ifstream 此数据类型表示输入文件流,用于从文件中读取信息。 |
3 | fstream 此数据类型通常表示文件流,并具有ofstream和ifstream的功能,这意味着它可以创建文件,将信息写入文件以及从文件读取信息。 |
要在C ++中执行文件处理,头文件
打开文件
必须先打开文件,然后才能从中读取或写入文件。 可以使用ofstream或fstream对象来打开文件进行写入。 并且ifstream对象仅用于打开文件以供阅读。
以下是open()函数的标准语法,它是fstream,ifstream和ofstream对象的成员。
void open(const char *filename, ios::openmode mode);
这里,第一个参数指定要打开的文件的名称和位置, open()成员函数的第二个参数定义应该打开文件的模式。
Sr.No | 模式标志和描述 |
---|---|
1 | ios::app 追加模式。 所有输出到该文件将附加到末尾。 |
2 | ios::ate 打开文件以进行输出,并将读/写控件移动到文件末尾。 |
3 | ios::in 打开文件进行阅读。 |
4 | ios::out 打开文件进行写作。 |
5 | ios::trunc 如果该文件已存在,则在打开文件之前将截断其内容。 |
您可以将两个或多个这些值组合在一起进行组合。 例如,如果您想以写入模式打开文件并希望在已经存在的情况下截断它,则以下将是语法 -
ofstream outfile;
outfile.open("file.dat", ios::out | ios::trunc );
类似地,您可以打开文件进行读写目的,如下所示 -
fstream afile;
afile.open("file.dat", ios::out | ios::in );
关闭一个文件 (Closing a File)
当C ++程序终止时,它会自动刷新所有流,释放所有已分配的内存并关闭所有打开的文件。 但是程序员应该在程序终止之前关闭所有打开的文件,这总是一个好习惯。
以下是close()函数的标准语法,它是fstream,ifstream和ofstream对象的成员。
void close();
写入文件
在进行C ++编程时,您可以使用流插入运算符(< 唯一的区别是你使用ofstream或fstream对象而不是cout对象。
从文件中读取 (Reading from a File)
您可以使用流提取运算符(>>)将文件中的信息读入程序,就像使用该运算符从键盘输入信息一样。 唯一的区别是你使用ifstream或fstream对象而不是cin对象。
读写示例
以下是C ++程序,它以读写模式打开文件。 在将用户输入的信息写入名为afile.dat的文件后,程序从文件中读取信息并将其输出到屏幕上 -
#include <fstream>
#include <iostream>
using namespace std;
int main () {
char data[100];
// open a file in write mode.
ofstream outfile;
outfile.open("afile.dat");
cout << "Writing to the file" << endl;
cout << "Enter your name: ";
cin.getline(data, 100);
// write inputted data into the file.
outfile << data << endl;
cout << "Enter your age: ";
cin >> data;
cin.ignore();
// again write inputted data into the file.
outfile << data << endl;
// close the opened file.
outfile.close();
// open a file in read mode.
ifstream infile;
infile.open("afile.dat");
cout << "Reading from the file" << endl;
infile >> data;
// write the data at the screen.
cout << data << endl;
// again read the data from the file and display it.
infile >> data;
cout << data << endl;
// close the opened file.
infile.close();
return 0;
}
编译并执行上述代码时,它会生成以下示例输入和输出 -
$./a.out
Writing to the file
Enter your name: Zara
Enter your age: 9
Reading from the file
Zara
9
上面的例子使用来自cin对象的附加函数,比如getline()函数从外部读取行,而ignore()函数则忽略前一个read语句留下的额外字符。
文件位置指针
istream和ostream提供了用于重新定位文件位置指针的成员函数。 这些成员函数是针对ostream的istream和seekp (“seek put”)的seekg (“seek get”)。
seekg和seekp的参数通常是一个长整数。 可以指定第二个参数来指示搜索方向。 搜索方向可以是ios::beg (默认值),用于相对于流的开头定位, ios::cur用于相对于流中的当前位置进行定位,或者ios::end用于相对于ios::end的定位流。
文件位置指针是一个整数值,它将文件中的位置指定为文件起始位置的字节数。 定位“get”文件位置指针的一些示例是 -
// 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++ Exception Handling
例外是在执行程序期间出现的问题。 C ++异常是对程序运行时出现的异常情况的响应,例如尝试除以零。
例外提供了一种将控制从程序的一个部分转移到另一个部分的方法。 C ++异常处理基于三个关键字: try, catch,和throw 。
throw - 当问题出现时,程序会抛出异常。 这是使用throw关键字完成的。
catch - 程序在程序中要处理问题的位置捕获异常并使用异常处理程序。 catch关键字表示捕获异常。
try - try块标识将激活特定异常的代码块。 接下来是一个或多个catch块。
假设块将引发异常,则方法使用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
}
如果try块在不同情况下引发多个异常,您可以列出多个catch语句以捕获不同类型的异常。
抛出异常
可以使用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类型的ExceptionName 。 如果要指定catch块应该处理try块中抛出的任何类型的异常,则必须在括起异常声明的括号之间放置省略号,如下所示 -
try {
// protected code
} catch(...) {
// code to handle any exception
}
以下是一个示例,它抛出除零异常,我们在catch块中捕获它。
#include <iostream>
using namespace std;
double division(int a, int b) {
if( b == 0 ) {
throw "Division by zero condition!";
}
return (a/b);
}
int main () {
int x = 50;
int y = 0;
double z = 0;
try {
z = division(x, y);
cout << z << endl;
} catch (const char* msg) {
cerr << msg << endl;
}
return 0;
}
因为我们引发了类型const char*的异常,所以在捕获此异常时,我们必须在catch块中使用const char *。 如果我们编译并运行上面的代码,这将产生以下结果 -
Division by zero condition!
C++ Standard Exceptions
C ++提供了《exception》定义的标准异常列表,我们可以在程序中使用它。 这些安排在如下所示的父子类层次结构中 -
以下是上述层次结构中提到的每个例外的小描述 -
Sr.No | 例外与描述 |
---|---|
1 | std::exception 所有标准C ++异常的异常和父类。 |
2 | std::bad_alloc 这可以被new抛出。 |
3 | std::bad_cast 这可以通过dynamic_cast抛出。 |
4 | std::bad_exception 这是处理C ++程序中意外异常的有用设备。 |
5 | std::bad_typeid 这可以通过typeid抛出。 |
6 | std::logic_error 理论上可以通过读取代码来检测到的异常。 |
7 | std::domain_error 当使用数学上无效的域时,抛出此异常。 |
8 | std::invalid_argument 由于参数无效而抛出此异常。 |
9 | std::length_error 当创建太大的std :: string时抛出此异常。 |
10 | std::out_of_range 这可以通过'at'方法抛出,例如std :: vector和std :: bitset <> :: operator []()。 |
11 | std::runtime_error 通过读取代码在理论上无法检测到的异常。 |
12 | std::overflow_error 如果发生数学溢出,则抛出此异常。 |
13 | std::range_error 当您尝试存储超出范围的值时会发生这种情况。 |
14 | std::underflow_error 如果发生数学下溢,则抛出此异常。 |
定义新例外
您可以通过继承和覆盖exception类功能来定义自己的异常。 以下是示例,其中显示了如何使用std :: exception类以标准方式实现自己的异常 -
#include <iostream>
#include <exception>
using namespace std;
struct MyException : public exception {
const char * what () const throw () {
return "C++ Exception";
}
};
int main() {
try {
throw MyException();
} catch(MyException& e) {
std::cout << "MyException caught" << std::endl;
std::cout << e.what() << std::endl;
} catch(std::exception& e) {
//Other errors
}
}
这会产生以下结果 -
MyException caught
C++ Exception
这里, what()是由异常类提供的公共方法,它已被所有子异常类覆盖。 这将返回异常的原因。
C++ Dynamic Memory
很好地理解动态内存如何在C ++中真正起作用对于成为一名优秀的C ++程序员至关重要。 C ++程序中的内存分为两部分 -
The stack - 在函数内声明的所有变量将从堆栈中占用内存。
The heap - 这是程序未使用的内存,可用于在程序运行时动态分配内存。
很多时候,您事先并不知道在特定变量中存储特定信息需要多少内存,并且可以在运行时确定所需内存的大小。
您可以使用C ++中的特殊运算符在堆内的运行时为给定类型的变量分配内存,该运算符返回分配的空间的地址。 此运算符称为new运算符。
如果您不再需要动态分配的内存,可以使用delete运算符,它取消分配以前由new运算符分配的内存。
new和delete运算符
以下通用语法使用new运算符为任何数据类型动态分配内存。
new data-type;
这里, data-type可以是任何内置数据类型,包括数组或任何用户定义的数据类型包括类或结构。 让我们从内置数据类型开始。 例如,我们可以定义一个指向double类型的指针,然后请求在执行时分配内存。 我们可以使用new运算符并使用以下语句执行此操作 -
double* pvalue = NULL; // Pointer initialized with null
pvalue = new double; // Request memory for the variable
如果免费商店已用完,则可能尚未成功分配内存。 因此,最好检查新运算符是否返回NULL指针并采取以下相应的操作 -
double* pvalue = NULL;
if( !(pvalue = new double )) {
cout << "Error: out of memory." <<endl;
exit(1);
}
来自C的malloc()函数仍然存在于C ++中,但建议避免使用malloc()函数。 new over malloc()的主要优点是new不只是分配内存,而是构造C ++主要目的的对象。
在任何时候,当您感觉不再需要动态分配的变量时,您可以使用'delete'运算符释放它在免费存储中占用的内存,如下所示 -
delete pvalue; // Release memory pointed to by pvalue
让我们将上述概念放在上面,并形成以下示例,以显示“新”和“删除”的工作原理 -
#include <iostream>
using namespace std;
int main () {
double* pvalue = NULL; // Pointer initialized with null
pvalue = new double; // Request memory for the variable
*pvalue = 29494.99; // Store value at allocated address
cout << "Value of pvalue : " << *pvalue << endl;
delete pvalue; // free up the memory.
return 0;
}
如果我们编译并运行上面的代码,这将产生以下结果 -
Value of pvalue : 29495
数组的动态内存分配
考虑您要为字符数组分配内存,即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
对象的动态内存分配
对象与简单数据类型没有区别。 例如,考虑以下代码,我们将使用对象数组来阐明概念 -
#include <iostream>
using namespace std;
class Box {
public:
Box() {
cout << "Constructor called!" <<endl;
}
~Box() {
cout << "Destructor called!" <<endl;
}
};
int main() {
Box* myBoxArray = new Box[4];
delete [] myBoxArray; // Delete array
return 0;
}
如果你要分配一个包含四个Box对象的数组,那么Simple构造函数将被调用四次,同样在删除这些对象时,析构函数也会被调用相同的次数。
如果我们编译并运行上面的代码,这将产生以下结果 -
Constructor called!
Constructor called!
Constructor called!
Constructor called!
Destructor called!
Destructor called!
Destructor called!
Destructor called!
Namespaces in C++
考虑一种情况,当我们在同一个class中有两个同名的人Zara时。 每当我们需要区分它们时,我们必须使用一些额外的信息以及它们的名称,如区域,如果它们居住在不同的区域或他们的母亲或父亲的名字等。
您的C ++应用程序中可能出现相同的情况。 例如,您可能正在编写一些具有名为xyz()的函数的代码,并且还有另一个库,它也具有相同的函数xyz()。 现在,编译器无法知道您在代码中引用的xyz()函数的版本。
namespace旨在克服这一困难,并用作额外信息,以区分不同库中可用的相同名称的类似函数,类,变量等。 使用命名空间,您可以定义定义名称的上下文。 实质上,命名空间定义了范围。
定义命名空间
命名空间定义以关键字namespace空间开头,后跟命名空间名称,如下所示 -
namespace namespace_name {
// code declarations
}
要调用函数或变量的启用命名空间的版本,请在(::)之前添加命名空间名称,如下所示 -
name::code; // code could be variable or function.
让我们看一下包含变量和函数的实体的命名空间范围 -
#include <iostream>
using namespace std;
// first name space
namespace first_space {
void func() {
cout << "Inside first_space" << endl;
}
}
// second name space
namespace second_space {
void func() {
cout << "Inside second_space" << endl;
}
}
int main () {
// Calls function from first name space.
first_space::func();
// Calls function from second name space.
second_space::func();
return 0;
}
如果我们编译并运行上面的代码,这将产生以下结果 -
Inside first_space
Inside second_space
using指令
您还可以using namespace指令避免using namespace前置。 该指令告诉编译器后续代码正在使用指定命名空间中的名称。 因此,命名空间隐含在以下代码中 -
#include <iostream>
using namespace std;
// first name space
namespace first_space {
void func() {
cout << "Inside first_space" << endl;
}
}
// second name space
namespace second_space {
void func() {
cout << "Inside second_space" << endl;
}
}
using namespace first_space;
int main () {
// This calls function from first name space.
func();
return 0;
}
如果我们编译并运行上面的代码,这将产生以下结果 -
Inside first_space
'using'指令也可用于引用命名空间中的特定项。 例如,如果您打算使用的std命名空间的唯一部分是cout,则可以按如下方式引用它 -
using std::cout;
后续代码可以引用cout而不预先添加命名空间,但是std命名空间中的其他项仍然需要显式如下 -
#include <iostream>
using std::cout;
int main () {
cout << "std::endl is used with std!" << std::endl;
return 0;
}
如果我们编译并运行上面的代码,这将产生以下结果 -
std::endl is used with 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,那么它将使范围内的namespace_name2元素可用,如下所示 -
#include <iostream>
using namespace std;
// first name space
namespace first_space {
void func() {
cout << "Inside first_space" << endl;
}
// second name space
namespace second_space {
void func() {
cout << "Inside second_space" << endl;
}
}
}
using namespace first_space::second_space;
int main () {
// This calls function from second name space.
func();
return 0;
}
如果我们编译并运行上面的代码,这将产生以下结果 -
Inside second_space
C++ Templates
模板是泛型编程的基础,它涉及以独立于任何特定类型的方式编写代码。
模板是用于创建泛型类或函数的蓝图或公式。 像迭代器和算法这样的库容器是通用编程的示例,并且是使用模板概念开发的。
每个容器都有一个定义,例如vector ,但我们可以定义许多不同类型的向量,例如vector 《int》或vector 《string》 。
您可以使用模板来定义函数和类,让我们看看它们是如何工作的 -
功能模板
此处显示模板函数定义的一般形式 -
template <class type> ret-type func-name(parameter list) {
// body of function
}
这里,type是函数使用的数据类型的占位符名称。 该名称可以在函数定义中使用。
以下是返回最多两个值的函数模板示例 -
#include <iostream>
#include <string>
using namespace std;
template <typename T>
inline T const& Max (T const& a, T const& b) {
return a < b ? b:a;
}
int main () {
int i = 39;
int j = 20;
cout << "Max(i, j): " << Max(i, j) << endl;
double f1 = 13.5;
double f2 = 20.7;
cout << "Max(f1, f2): " << Max(f1, f2) << endl;
string s1 = "Hello";
string s2 = "World";
cout << "Max(s1, s2): " << Max(s1, s2) << endl;
return 0;
}
如果我们编译并运行上面的代码,这将产生以下结果 -
Max(i, j): 39
Max(f1, f2): 20.7
Max(s1, s2): World
类模板
就像我们可以定义函数模板一样,我们也可以定义类模板。 这里显示了泛型类声明的一般形式 -
template <class type> class class-name {
.
.
.
}
这里, type是占位符类型名称,将在实例化类时指定。 您可以使用逗号分隔列表定义多个通用数据类型。
以下是定义类Stack <>并实现泛型方法以从堆栈中推送和弹出元素的示例 -
#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <stdexcept>
using namespace std;
template <class T>
class Stack {
private:
vector<T> elems; // elements
public:
void push(T const&); // push element
void pop(); // pop element
T top() const; // return top element
bool empty() const { // return true if empty.
return elems.empty();
}
};
template <class T>
void Stack<T>::push (T const& elem) {
// append copy of passed element
elems.push_back(elem);
}
template <class T>
void Stack<T>::pop () {
if (elems.empty()) {
throw out_of_range("Stack<>::pop(): empty stack");
}
// remove last element
elems.pop_back();
}
template <class T>
T Stack<T>::top () const {
if (elems.empty()) {
throw out_of_range("Stack<>::top(): empty stack");
}
// return copy of last element
return elems.back();
}
int main() {
try {
Stack<int> intStack; // stack of ints
Stack<string> stringStack; // stack of strings
// manipulate int stack
intStack.push(7);
cout << intStack.top() <<endl;
// manipulate string stack
stringStack.push("hello");
cout << stringStack.top() << std::endl;
stringStack.pop();
stringStack.pop();
} catch (exception const& ex) {
cerr << "Exception: " << ex.what() <<endl;
return -1;
}
}
如果我们编译并运行上面的代码,这将产生以下结果 -
7
hello
Exception: Stack<>::pop(): empty stack
C++ Preprocessor
预处理器是指令,它指示编译器在实际编译开始之前预处理信息。
所有预处理程序指令都以#开头,并且只有空格字符可能出现在一行上的预处理程序指令之前。 预处理程序指令不是C ++语句,因此它们不以分号(;)结尾。
您已经在所有示例中看到了#include指令。 此宏用于将头文件包含到源文件中。
C ++支持许多预处理程序指令,如#include,#define,#if,#else,#line等。让我们看看重要的指令 -
#define预处理器 (The #define Preprocessor)
#define预处理程序指令创建符号常量。 符号常量称为macro ,指令的一般形式是 -
#define macro-name replacement-text
当此行出现在文件中时,在编译程序之前,该文件中随后出现的所有宏都将被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;
}
Function-Like Macros
您可以使用#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;
}
如果我们编译并运行上面的代码,这将产生以下结果 -
The minimum is 30
条件编译
有几个指令,可用于编译程序源代码的选择部分。 此过程称为条件编译。
条件预处理器结构很像'if'选择结构。 考虑以下预处理器代码 -
#ifndef NULL
#define NULL 0
#endif
您可以编译程序以进行调试。 您还可以使用单个宏打开或关闭调试,如下所示 -
#ifdef DEBUG
cerr <<"Variable x = " << x << endl;
#endif
如果在指令#ifdef DEBUG之前定义了符号常量DEBUG,则会导致在程序中编译cerr语句。 您可以使用#if 0 statment注释掉程序的一部分,如下所示 -
#if 0
code prevented from compiling
#endif
让我们尝试以下示例 -
#include <iostream>
using namespace std;
#define DEBUG
#define MIN(a,b) (((a)<(b)) ? a : b)
int main () {
int i, j;
i = 100;
j = 30;
#ifdef DEBUG
cerr <<"Trace: Inside main function" << endl;
#endif
#if 0
/* This is commented part */
cout << MKSTR(HELLO C++) << endl;
#endif
cout <<"The minimum is " << MIN(i, j) << endl;
#ifdef DEBUG
cerr <<"Trace: Coming out of main function" << endl;
#endif
return 0;
}
如果我们编译并运行上面的代码,这将产生以下结果 -
The minimum is 30
Trace: Inside main function
Trace: Coming out of main function
#和##运算符
#和##预处理程序运算符在C ++和ANSI/ISO C中可用。#运算符使替换文本标记转换为由引号括起的字符串。
考虑以下宏定义 -
#include <iostream>
using namespace std;
#define MKSTR( x ) #x
int main () {
cout << MKSTR(HELLO C++) << endl;
return 0;
}
如果我们编译并运行上面的代码,这将产生以下结果 -
HELLO C++
让我们看看它是如何工作的。 很容易理解C ++预处理器会改变这一行 -
cout << MKSTR(HELLO C++) << endl;
以上行将变为以下行 -
cout << "HELLO C++" << endl;
##运算符用于连接两个标记。 这是一个例子 -
#define CONCAT( x, y ) x ## y
当CONCAT出现在程序中时,其参数被连接并用于替换宏。 例如,CONCAT(HELLO,C ++)在程序中被“HELLO C ++”替换如下。
#include <iostream>
using namespace std;
#define concat(a, b) a ## b
int main() {
int xy = 100;
cout << concat(x, y);
return 0;
}
如果我们编译并运行上面的代码,这将产生以下结果 -
100
让我们看看它是如何工作的。 很容易理解C ++预处理器转换 -
cout << concat(x, y);
以上行将转换为以下行 -
cout << xy;
预定义的C ++宏
C ++提供了下面提到的许多预定义宏 -
Sr.No | 宏观和描述 |
---|---|
1 | __LINE__ 它包含编译时程序的当前行号。 |
2 | __FILE__ 它包含编译时程序的当前文件名。 |
3 | __DATE__ 它包含一个月/日/年形式的字符串,它是将源文件转换为目标代码的日期。 |
4 | __TIME__ 它包含一个形式为hour:minute:second的字符串,它是编译程序的时间。 |
让我们看一下上述所有宏的示例 -
#include <iostream>
using namespace std;
int main () {
cout << "Value of __LINE__ : " << __LINE__ << endl;
cout << "Value of __FILE__ : " << __FILE__ << endl;
cout << "Value of __DATE__ : " << __DATE__ << endl;
cout << "Value of __TIME__ : " << __TIME__ << endl;
return 0;
}
如果我们编译并运行上面的代码,这将产生以下结果 -
Value of __LINE__ : 6
Value of __FILE__ : test.cpp
Value of __DATE__ : Feb 28 2011
Value of __TIME__ : 18:52:48
C++ Signal Handling
信号是操作系统传递给进程的中断,它可以过早地终止程序。 您可以通过在UNIX,LINUX,Mac OS X或Windows系统上按Ctrl + C来生成中断。
有一些信号无法被程序捕获,但有一个信号列表,您可以在程序中捕获并根据信号采取适当的措施。 这些信号在C ++头文件
Sr.No | 信号和描述 |
---|---|
1 | SIGABRT 程序异常终止,例如abort调用。 |
2 | SIGFPE 错误的算术运算,例如除以零或导致溢出的运算。 |
3 | SIGILL 检测非法指令。 |
4 | SIGINT 收到交互式注意信号。 |
5 | SIGSEGV 对存储的无效访问。 |
6 | SIGTERM 发送到程序的终止请求。 |
The signal() Function
C ++信号处理库提供捕获意外事件的函数signal 。 以下是signal()函数的语法 -
void (*signal (int sig, void (*func)(int)))(int);
保持简单,该函数接收两个参数:第一个参数作为表示信号编号的整数,第二个参数作为指向信号处理函数的指针。
让我们编写一个简单的C ++程序,我们将使用signal()函数捕获SIGINT信号。 无论您希望在程序中捕获什么信号,都必须使用signal函数注册该信号并将其与信号处理程序关联。 检查以下示例 -
#include <iostream>
#include <csignal>
using namespace std;
void signalHandler( int signum ) {
cout << "Interrupt signal (" << signum << ") received.\n";
// cleanup and close up stuff here
// terminate program
exit(signum);
}
int main () {
// register signal SIGINT and signal handler
signal(SIGINT, signalHandler);
while(1) {
cout << "Going to sleep...." << endl;
sleep(1);
}
return 0;
}
编译并执行上述代码时,会产生以下结果 -
Going to sleep....
Going to sleep....
Going to sleep....
现在,按Ctrl + c中断程序,你会看到你的程序将捕获信号,并通过打印如下出来 -
Going to sleep....
Going to sleep....
Going to sleep....
Interrupt signal (2) received.
The raise() Function
您可以通过函数raise()生成信号,该函数采用整数信号编号作为参数,并具有以下语法。
int raise (signal sig);
这里, sig是发送任何信号的信号编号:SIGINT,SIGABRT,SIGFPE,SIGILL,SIGSEGV,SIGTERM,SIGHUP。 以下是我们使用raise()函数在内部引发信号的示例,如下所示 -
#include <iostream>
#include <csignal>
using namespace std;
void signalHandler( int signum ) {
cout << "Interrupt signal (" << signum << ") received.\n";
// cleanup and close up stuff here
// terminate program
exit(signum);
}
int main () {
int i = 0;
// register signal SIGINT and signal handler
signal(SIGINT, signalHandler);
while(++i) {
cout << "Going to sleep...." << endl;
if( i == 3 ) {
raise( SIGINT);
}
sleep(1);
}
return 0;
}
编译并执行上述代码时,会产生以下结果并自动生成 -
Going to sleep....
Going to sleep....
Going to sleep....
Interrupt signal (2) received.
C++ Multithreading
多线程是多任务处理的一种特殊形式,多任务处理功能允许您的计算机同时运行两个或多个程序。 通常,有两种类型的多任务处理:基于进程和基于线程。
基于进程的多任务处理程序的并发执行。 基于线程的多任务处理同时执行同一程序的各个部分。
多线程程序包含两个或多个可以并发运行的部分。 这样的程序的每个部分称为线程,每个线程定义一个单独的执行路径。
C ++不包含对多线程应用程序的任何内置支持。 相反,它完全依赖于操作系统来提供此功能。
本教程假设您正在使用Linux OS,我们将使用POSIX编写多线程C ++程序。 POSIX Threads或Pthreads提供的API可用于许多类似Unix的POSIX系统,如FreeBSD,NetBSD,GNU/Linux,Mac OS X和Solaris。
创建线程
以下例程用于创建POSIX线程 -
#include <pthread.h>
pthread_create (thread, attr, start_routine, arg)
这里, pthread_create创建一个新线程并使其可执行。 可以在代码中的任何位置任意调用此例程。 以下是参数的说明 -
Sr.No | 参数和描述 |
---|---|
1 | thread 子例程返回的新线程的不透明唯一标识符。 |
2 | attr 一个opaque属性对象,可用于设置线程属性。 您可以指定线程属性对象,或者为默认值指定NULL。 |
3 | start_routine 线程创建后将执行的C ++例程。 |
4 | arg 可以传递给start_routine的单个参数。 它必须通过引用传递为void类型的指针转换。 如果不传递参数,则可以使用NULL。 |
进程可以创建的最大线程数取决于实现。 一旦创建,线程就是对等的,并且可以创建其他线程。 线程之间没有隐含的层次结构或依赖关系。
终止线程
我们使用以下例程来终止POSIX线程 -
#include <pthread.h>
pthread_exit (status)
这里pthread_exit用于显式退出线程。 通常,在线程完成其工作并且不再需要存在之后调用pthread_exit()例程。
如果main()在它创建的线程之前完成,并且使用pthread_exit()退出,则其他线程将继续执行。 否则,当main()完成时,它们将自动终止。
Example
这个简单的示例代码使用pthread_create()例程创建了5个线程。 每个主题都打印出一个“Hello World!” 消息,然后通过调用pthread_exit()终止。
#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
void *PrintHello(void *threadid) {
long tid;
tid = (long)threadid;
cout << "Hello World! Thread ID, " << tid << endl;
pthread_exit(NULL);
}
int main () {
pthread_t threads[NUM_THREADS];
int rc;
int i;
for( i = 0; i < NUM_THREADS; i++ ) {
cout << "main() : creating thread, " << i << endl;
rc = pthread_create(&threads[i], NULL, PrintHello, (void *)i);
if (rc) {
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}
pthread_exit(NULL);
}
使用-lpthread库编译以下程序,如下所示 -
$gcc test.cpp -lpthread
现在,执行您的程序,它提供以下输出 -
main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Hello World! Thread ID, 0
Hello World! Thread ID, 1
Hello World! Thread ID, 2
Hello World! Thread ID, 3
Hello World! Thread ID, 4
将参数传递给线程
此示例显示如何通过结构传递多个参数。 您可以在线程回调中传递任何数据类型,因为它指向void,如以下示例中所述 -
#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
struct thread_data {
int thread_id;
char *message;
};
void *PrintHello(void *threadarg) {
struct thread_data *my_data;
my_data = (struct thread_data *) threadarg;
cout << "Thread ID : " << my_data->thread_id ;
cout << " Message : " << my_data->message << endl;
pthread_exit(NULL);
}
int main () {
pthread_t threads[NUM_THREADS];
struct thread_data td[NUM_THREADS];
int rc;
int i;
for( i = 0; i < NUM_THREADS; i++ ) {
cout <<"main() : creating thread, " << i << endl;
td[i].thread_id = i;
td[i].message = "This is message";
rc = pthread_create(&threads[i], NULL, PrintHello, (void *)&td[i]);
if (rc) {
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}
pthread_exit(NULL);
}
编译并执行上述代码时,会产生以下结果 -
main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Thread ID : 3 Message : This is message
Thread ID : 2 Message : This is message
Thread ID : 0 Message : This is message
Thread ID : 1 Message : This is message
Thread ID : 4 Message : This is message
加入和分离线程
我们可以使用以下两个例程来连接或分离线程 -
pthread_join (threadid, status)
pthread_detach (threadid)
pthread_join()子例程阻塞调用线程,直到指定的threadid线程终止。 创建线程时,其中一个属性定义它是可连接还是已分离。 只能创建作为可连接创建的线程。 如果将线程创建为已分离,则永远不会将其连接。
此示例演示如何使用Pthread连接例程等待线程完成。
#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>
using namespace std;
#define NUM_THREADS 5
void *wait(void *t) {
int i;
long tid;
tid = (long)t;
sleep(1);
cout << "Sleeping in thread " << endl;
cout << "Thread with id : " << tid << " ...exiting " << endl;
pthread_exit(NULL);
}
int main () {
int rc;
int i;
pthread_t threads[NUM_THREADS];
pthread_attr_t attr;
void *status;
// Initialize and set thread joinable
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
for( i = 0; i < NUM_THREADS; i++ ) {
cout << "main() : creating thread, " << i << endl;
rc = pthread_create(&threads[i], attr, wait, (void *)i );
if (rc) {
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}
// free attribute and wait for the other threads
pthread_attr_destroy(&attr);
for( i = 0; i < NUM_THREADS; i++ ) {
rc = pthread_join(threads[i], &status);
if (rc) {
cout << "Error:unable to join," << rc << endl;
exit(-1);
}
cout << "Main: completed thread id :" << i ;
cout << " exiting with status :" << status << endl;
}
cout << "Main: program exiting." << endl;
pthread_exit(NULL);
}
编译并执行上述代码时,会产生以下结果 -
main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Sleeping in thread
Thread with id : 0 .... exiting
Sleeping in thread
Thread with id : 1 .... exiting
Sleeping in thread
Thread with id : 2 .... exiting
Sleeping in thread
Thread with id : 3 .... exiting
Sleeping in thread
Thread with id : 4 .... exiting
Main: completed thread id :0 exiting with status :0
Main: completed thread id :1 exiting with status :0
Main: completed thread id :2 exiting with status :0
Main: completed thread id :3 exiting with status :0
Main: completed thread id :4 exiting with status :0
Main: program exiting.
C++ Web Programming
什么是CGI?
通用网关接口(CGI)是一组标准,用于定义如何在Web服务器和自定义脚本之间交换信息。
CGI规范目前由NCSA维护,NCSA定义CGI如下 -
通用网关接口(CGI)是外部网关程序的标准,用于与信息服务器(如HTTP服务器)连接。
目前的版本是CGI/1.1,CGI/1.2正在进行中。
网页浏览 (Web Browsing)
为了理解CGI的概念,让我们看看当我们点击超链接浏览特定网页或URL时会发生什么。
您的浏览器联系HTTP Web服务器并要求URL即。 文件名。
Web Server将解析URL并查找文件名。 如果找到所请求的文件,则Web服务器将该文件发送回浏览器,否则会发送一条错误消息,指出您已请求错误的文件。
Web浏览器从Web服务器获取响应,并根据收到的响应显示接收的文件或错误消息。
但是,可以以这样的方式设置HTTP服务器:每当请求某个目录中的文件时,该文件不会被发回; 而是将其作为程序执行,并将程序生成的输出发送回浏览器进行显示。
通用网关接口(CGI)是一种标准协议,用于使应用程序(称为CGI程序或CGI脚本)与Web服务器和客户端进行交互。 这些CGI程序可以用Python,PERL,Shell,C或C ++等编写。
CGI架构图 (CGI Architecture Diagram)
以下简单程序显示了CGI的简单架构 -
Web服务器配置
在继续进行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等。
第一个CGI程序 (First CGI Program)
考虑以下C ++程序内容 -
#include <iostream>
using namespace std;
int main () {
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>Hello World - First CGI Program</title>\n";
cout << "</head>\n";
cout << "<body>\n";
cout << "<h2>Hello World! This is my first CGI program</h2>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
编译上面的代码并将可执行文件命名为cplusplus.cgi。 此文件保存在/ var/www/cgi-bin目录中,并具有以下内容。 在运行CGI程序之前,请确保使用chmod 755 cplusplus.cgi UNIX命令更改文件模式以使文件可执行。
我的第一个CGI计划
上面的C ++程序是一个简单的程序,它将输出写在STDOUT文件即屏幕上。 有一个重要的额外功能,即第一行打印Content-type:text/html\r\n\r\n 。 此行将发送回浏览器并指定要在浏览器屏幕上显示的内容类型。 现在您必须已经理解了CGI的基本概念,并且您可以使用Python编写许多复杂的CGI程序。 C ++ CGI程序可以与任何其他外部系统(如RDBMS)交互以交换信息。
HTTP标头
Content-type:text/html\r\n\r\n是HTTP标头的一部分,它被发送到浏览器以了解内容。 所有HTTP标头将采用以下形式 -
HTTP Field Name: Field Content
For Example
Content-type: text/html\r\n\r\n
很少有其他重要的HTTP标头,您将在CGI编程中经常使用它们。
Sr.No | 标题和说明 |
---|---|
1 | Content-type: MIME字符串,用于定义要返回的文件的格式。 示例是Content-type:text/html。 |
2 | Expires: Date 信息失效的日期。 浏览器应该使用它来决定何时需要刷新页面。 有效日期字符串的格式应为01 Jan 1998 12:00:00 GMT。 |
3 | Location: URL 应返回的URL而不是请求的URL。 您可以使用此字段将请求重定向到任何文件。 |
4 | Last-modified: Date 上次修改资源的日期。 |
5 | Content-length: N 返回数据的长度(以字节为单位)。 浏览器使用此值报告文件的估计下载时间。 |
6 | Set-Cookie: String 设置通过string传递的cookie。 |
CGI环境变量 (CGI Environment Variables)
所有CGI程序都可以访问以下环境变量。 在编写任何CGI程序时,这些变量都起着重要作用。
Sr.No | 变量名称和描述 |
---|---|
1 | CONTENT_TYPE 内容的数据类型,在客户端将附加内容发送到服务器时使用。 例如文件上传等。 |
2 | CONTENT_LENGTH 仅适用于POST请求的查询信息的长度。 |
3 | HTTP_COOKIE 以键和值对的形式返回设置的cookie。 |
4 | HTTP_USER_AGENT User-Agent请求标头字段包含有关发起请求的用户代理的信息。 它是Web浏览器的名称。 |
5 | PATH_INFO CGI脚本的路径。 |
6 | QUERY_STRING 使用GET方法请求发送的URL编码信息。 |
7 | REMOTE_ADDR 发出请求的远程主机的IP地址。 这对于日志记录或身份验证非常有用。 |
8 | REMOTE_HOST 发出请求的主机的完全限定名称。 如果此信息不可用,则可以使用REMOTE_ADDR获取IR地址。 |
9 | REQUEST_METHOD 用于发出请求的方法。 最常用的方法是GET和POST。 |
10 | SCRIPT_FILENAME CGI脚本的完整路径。 |
11 | SCRIPT_NAME CGI脚本的名称。 |
12 | SERVER_NAME 服务器的主机名或IP地址。 |
13 | SERVER_SOFTWARE 服务器正在运行的软件的名称和版本。 |
这是一个小型CGI程序,列出了所有CGI变量。
#include <iostream>
#include <stdlib.h>
using namespace std;
const string ENV[ 24 ] = {
"COMSPEC", "DOCUMENT_ROOT", "GATEWAY_INTERFACE",
"HTTP_ACCEPT", "HTTP_ACCEPT_ENCODING",
"HTTP_ACCEPT_LANGUAGE", "HTTP_CONNECTION",
"HTTP_HOST", "HTTP_USER_AGENT", "PATH",
"QUERY_STRING", "REMOTE_ADDR", "REMOTE_PORT",
"REQUEST_METHOD", "REQUEST_URI", "SCRIPT_FILENAME",
"SCRIPT_NAME", "SERVER_ADDR", "SERVER_ADMIN",
"SERVER_NAME","SERVER_PORT","SERVER_PROTOCOL",
"SERVER_SIGNATURE","SERVER_SOFTWARE" };
int main () {
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>CGI Environment Variables</title>\n";
cout << "</head>\n";
cout << "<body>\n";
cout << "<table border = \"0\" cellspacing = \"2\">";
for ( int i = 0; i < 24; i++ ) {
cout << "<tr><td>" << ENV[ i ] << "</td><td>";
// attempt to retrieve value of environment variable
char *value = getenv( ENV[ i ].c_str() );
if ( value != 0 ) {
cout << value;
} else {
cout << "Environment variable does not exist.";
}
cout << "</td></tr>\n";
}
cout << "</table><\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
C++ CGI Library
对于实际示例,您需要通过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
您可以查看“C ++ CGI Lib文档”中提供的相关文档。
GET和POST方法 (GET and POST Methods)
当您需要将某些信息从浏览器传递到Web服务器并最终传递到CGI程序时,您必须遇到很多情况。 最常见的浏览器使用两种方法将此信息传递给Web服务器。 这些方法是GET方法和POST方法。
使用GET方法传递信息
GET方法发送附加到页面请求的编码用户信息。 页面和编码信息由?分隔。 性格如下 -
http://www.test.com/cgi-bin/cpp.cgi?key1=value1&key2=value2
GET方法是将信息从浏览器传递到Web服务器的默认方法,它会生成一个长字符串,显示在浏览器的Location:框中。 如果您要将密码或其他敏感信息传递给服务器,请勿使用GET方法。 GET方法具有大小限制,您可以在请求字符串中传递最多1024个字符。
使用GET方法时,使用QUERY_STRING http标头传递信息,并可通过QUERY_STRING环境变量在CGI程序中访问。
您可以通过简单地将键和值对与任何URL连接来传递信息,也可以使用HTML
简单URL示例:获取方法
这是一个简单的URL,它将使用GET方法将两个值传递给hello_get.py程序。
/cgi-bin/cpp_get.cgi?first_name=ZARA&last_name=ALI下面是一个生成cpp_get.cgi CGI程序的程序,用于处理Web浏览器给出的输入。 我们将使用C ++ CGI库,这使得访问传递信息变得非常容易 -
#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <cgicc/CgiDefs.h>
#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
using namespace std;
using namespace cgicc;
int main () {
Cgicc formData;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>Using GET and POST Methods</title>\n";
cout << "</head>\n";
cout << "<body>\n";
form_iterator fi = formData.getElement("first_name");
if( !fi->isEmpty() && fi != (*formData).end()) {
cout << "First name: " << **fi << endl;
} else {
cout << "No text entered for first name" << endl;
}
cout << "<br/>\n";
fi = formData.getElement("last_name");
if( !fi->isEmpty() &&fi != (*formData).end()) {
cout << "Last name: " << **fi << endl;
} else {
cout << "No text entered for last name" << endl;
}
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
现在,按如下方式编译上述程序 -
$g++ -o cpp_get.cgi cpp_get.cpp -lcgicc
生成cpp_get.cgi并将其放入CGI目录并尝试使用以下链接进行访问 -
/cgi-bin/cpp_get.cgi?first_name=ZARA&last_name=ALI这将产生以下结果 -
First name: ZARA
Last name: ALI
简单形式示例:GET方法
这是一个使用HTML FORM和提交按钮传递两个值的简单示例。 我们将使用相同的CGI脚本cpp_get.cgi来处理此输入。
<form action = "/cgi-bin/cpp_get.cgi" method = "get">
First Name: <input type = "text" name = "first_name"> <br />
Last Name: <input type = "text" name = "last_name" />
<input type = "submit" value = "Submit" />
</form>
以下是上述表格的实际输出。 输入名字和姓氏,然后单击“提交”按钮以查看结果。
使用POST方法传递信息
通常更可靠的将信息传递给CGI程序的方法是POST方法。 这包装信息的方式与GET方法完全相同,但不是在文本字符串之后将其作为文本字符串发送? 在URL中,它将其作为单独的消息发送。 此消息以标准输入的形式进入CGI脚本。
同样的cpp_get.cgi程序也将处理POST方法。 让我们采用与上面相同的例子,它使用HTML FORM和提交按钮传递两个值,但这次使用POST方法如下 -
<form action = "/cgi-bin/cpp_get.cgi" method = "post">
First Name: <input type = "text" name = "first_name"><br />
Last Name: <input type = "text" name = "last_name" />
<input type = "submit" value = "Submit" />
</form>
以下是上述表格的实际输出。 输入名字和姓氏,然后单击“提交”按钮以查看结果。
将复选框数据传递给CGI程序
如果需要选择多个选项,则使用复选框。
以下是带有两个复选框的表单的示例HTML代码 -
<form action = "/cgi-bin/cpp_checkbox.cgi" method = "POST" target = "_blank">
<input type = "checkbox" name = "maths" value = "on" /> Maths
<input type = "checkbox" name = "physics" value = "on" /> Physics
<input type = "submit" value = "Select Subject" />
</form>
此代码的结果如下:
下面是C ++程序,它将生成cpp_checkbox.cgi脚本来处理Web浏览器通过复选框按钮给出的输入。
#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <cgicc/CgiDefs.h>
#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
using namespace std;
using namespace cgicc;
int main () {
Cgicc formData;
bool maths_flag, physics_flag;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>Checkbox Data to CGI</title>\n";
cout << "</head>\n";
cout << "<body>\n";
maths_flag = formData.queryCheckbox("maths");
if( maths_flag ) {
cout << "Maths Flag: ON " << endl;
} else {
cout << "Maths Flag: OFF " << endl;
}
cout << "<br/>\n";
physics_flag = formData.queryCheckbox("physics");
if( physics_flag ) {
cout << "Physics Flag: ON " << endl;
} else {
cout << "Physics Flag: OFF " << endl;
}
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
将单选按钮数据传递给CGI程序
当只需要选择一个选项时,使用单选按钮。
以下是带有两个单选按钮的表单的示例HTML代码 -
<form action = "/cgi-bin/cpp_radiobutton.cgi" method = "post" target = "_blank">
<input type = "radio" name = "subject" value = "maths" checked = "checked"/> Maths
<input type = "radio" name = "subject" value = "physics" /> Physics
<input type = "submit" value = "Select Subject" />
</form>
此代码的结果如下:
下面是C ++程序,它将生成cpp_radiobutton.cgi脚本来处理Web浏览器通过单选按钮给出的输入。
#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <cgicc/CgiDefs.h>
#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
using namespace std;
using namespace cgicc;
int main () {
Cgicc formData;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>Radio Button Data to CGI</title>\n";
cout << "</head>\n";
cout << "<body>\n";
form_iterator fi = formData.getElement("subject");
if( !fi->isEmpty() && fi != (*formData).end()) {
cout << "Radio box selected: " << **fi << endl;
}
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
将文本区域数据传递给CGI程序
当多行文本必须传递给CGI程序时,使用TEXTAREA元素。
以下是带有TEXTAREA框的表单的示例HTML代码 -
<form action = "/cgi-bin/cpp_textarea.cgi" method = "post" target = "_blank">
<textarea name = "textcontent" cols = "40" rows = "4">
Type your text here...
</textarea>
<input type = "submit" value = "Submit" />
</form>
此代码的结果如下:
下面是C ++程序,它将生成cpp_textarea.cgi脚本来处理Web浏览器通过文本区域给出的输入。
#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <cgicc/CgiDefs.h>
#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
using namespace std;
using namespace cgicc;
int main () {
Cgicc formData;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>Text Area Data to CGI</title>\n";
cout << "</head>\n";
cout << "<body>\n";
form_iterator fi = formData.getElement("textcontent");
if( !fi->isEmpty() && fi != (*formData).end()) {
cout << "Text Content: " << **fi << endl;
} else {
cout << "No text entered" << endl;
}
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
将下拉框数据传递给CGI程序
当我们有许多选项但只选择一个或两个时,使用下拉框。
以下是带有一个下拉框的表单的示例HTML代码 -
<form action = "/cgi-bin/cpp_dropdown.cgi" method = "post" target = "_blank">
<select name = "dropdown">
<option value = "Maths" selected>Maths</option>
<option value = "Physics">Physics</option>
</select>
<input type = "submit" value = "Submit"/>
</form>
此代码的结果如下:
下面是C ++程序,它将生成cpp_dropdown.cgi脚本来处理Web浏览器通过下拉框给出的输入。
#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <cgicc/CgiDefs.h>
#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
using namespace std;
using namespace cgicc;
int main () {
Cgicc formData;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>Drop Down Box Data to CGI</title>\n";
cout << "</head>\n";
cout << "<body>\n";
form_iterator fi = formData.getElement("dropdown");
if( !fi->isEmpty() && fi != (*formData).end()) {
cout << "Value Selected: " << **fi << endl;
}
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
在CGI中使用cookie (Using Cookies in CGI)
HTTP协议是无状态协议。 但是对于商业网站,需要在不同页面之间维护会话信息。 例如,一个用户注册在完成许多页面后结束。 但是如何在所有网页上维护用户的会话信息。
在许多情况下,使用Cookie是记住和跟踪偏好,购买,佣金以及更好的访问者体验或网站统计所需的其他信息的最有效方法。
它是如何运作的 (How It Works)
您的服务器以cookie的形式向访问者的浏览器发送一些数据。 浏览器可以接受cookie。 如果是,则将其作为纯文本记录存储在访问者的硬盘上。 现在,当访问者到达您网站上的另一个页面时,该Cookie可供检索。 检索后,您的服务器知道/记住存储的内容。
Cookie是5个可变长度字段的纯文本数据记录 -
Expires - 显示cookie过期的日期。 如果这是空白,则访问者退出浏览器时cookie将过期。
Domain - 显示您网站的域名。
Path - 显示设置cookie的目录或网页的路径。 如果要从任何目录或页面检索cookie,这可能为空。
Secure - 如果此字段包含单词“secure”,则只能使用安全服务器检索cookie。 如果此字段为空,则不存在此类限制。
Name = Value - 以键和值对的形式设置和检索Cookie。
设置Cookies (Setting up Cookies)
将cookie发送到浏览器非常容易。 这些cookie将在Content-type字段之前与HTTP Header一起发送。 假设您要将UserID和密码设置为cookie。 因此,cookie设置将按如下方式进行
#include <iostream>
using namespace std;
int main () {
cout << "Set-Cookie:UserID = XYZ;\r\n";
cout << "Set-Cookie:Password = XYZ123;\r\n";
cout << "Set-Cookie:Domain = www.iowiki.com;\r\n";
cout << "Set-Cookie:Path = /perl;\n";
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>Cookies in CGI</title>\n";
cout << "</head>\n";
cout << "<body>\n";
cout << "Setting cookies" << endl;
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
从这个例子中,您必须了解如何设置cookie。 我们使用Set-Cookie HTTP标头来设置cookie。
在这里,可以选择设置诸如Expires,Domain和Path之类的cookie属性。 值得注意的是,在发送魔术行之前设置了cookie "Content-type:text/html\r\n\r\n 。
编译以上程序以生成setcookies.cgi,并尝试使用以下链接设置cookie。 它将在您的计算机上设置四个cookie -
/cgi-bin/setcookies.cgi
检索Cookies (Retrieving Cookies)
很容易检索所有设置的cookie。 Cookie存储在CGI环境变量HTTP_COOKIE中,它们将具有以下形式。
key1 = value1; key2 = value2; key3 = value3....
以下是如何检索Cookie的示例。
#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <cgicc/CgiDefs.h>
#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
using namespace std;
using namespace cgicc;
int main () {
Cgicc cgi;
const_cookie_iterator cci;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>Cookies in CGI</title>\n";
cout << "</head>\n";
cout << "<body>\n";
cout << "<table border = \"0\" cellspacing = \"2\">";
// get environment variables
const CgiEnvironment& env = cgi.getEnvironment();
for( cci = env.getCookieList().begin();
cci != env.getCookieList().end();
++cci ) {
cout << "<tr><td>" << cci->getName() << "</td><td>";
cout << cci->getValue();
cout << "</td></tr>\n";
}
cout << "</table><\n";
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
现在,编译上面的程序来生成getcookies.cgi,并尝试获取计算机上所有可用cookie的列表 -
/cgi-bin/getcookies.cgi
这将生成上一部分中设置的所有四个cookie以及计算机中设置的所有其他cookie的列表 -
UserID XYZ
Password XYZ123
Domain www.iowiki.com
Path /perl
文件上传示例
要上传文件,HTML表单必须将enctype属性设置为multipart/form-data 。 具有文件类型的输入标记将创建“浏览”按钮。
<html>
<body>
<form enctype = "multipart/form-data" action = "/cgi-bin/cpp_uploadfile.cgi"
method = "post">
<p>File: <input type = "file" name = "userfile" /></p>
<p><input type = "submit" value = "Upload" /></p>
</form>
</body>
</html>
此代码的结果如下:
Note - 以上示例已被故意禁用,以阻止人们在我们的服务器上上传文件。 但您可以尝试使用您的服务器上面的代码。
这是用于处理文件上载的脚本cpp_uploadfile.cpp -
#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <cgicc/CgiDefs.h>
#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
using namespace std;
using namespace cgicc;
int main () {
Cgicc cgi;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>File Upload in CGI</title>\n";
cout << "</head>\n";
cout << "<body>\n";
// get list of files to be uploaded
const_file_iterator file = cgi.getFile("userfile");
if(file != cgi.getFiles().end()) {
// send data type at cout.
cout << HTTPContentHeader(file->getDataType());
// write content at cout.
file->writeToStream(cout);
}
cout << "<File uploaded successfully>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
上面的示例用于在cout流中编写内容,但您可以打开文件流并将上载文件的内容保存在所需位置的文件中。