C++ 简介
C++ 是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化编程、面向对象编程和泛型编程。是 C 的一个超集,事实上,任何合法的 C 程序都是合法的 C++ 程序。
注意:使用静态类型的编程语言是在编译时执行类型检查,而不是在运行时执行类型检查。
标准的 C++ 由三个重要部分组成:
- 核心语言,提供了所有构件块,包括变量、数据类型和常量,等等。
- C++ 标准库,提供了大量的函数,用于操作文件、字符串等。
- 标准模板库(STL),提供了大量的方法,用于操作数据结构等。
环境设置
- 文本编辑器
- C++ 编译器:GNU 的 gcc 编译器
MAC OSX
下载 Xcode 开发环境,并按照安装说明进行安装。一旦安装上 Xcode,您就能使用 GNU 编译器。
Xcode 目前可从 developer.apple.com/technologies/tools/ 上下载。
数据类型
#include <iostream>
using namespace std;
// main() 是程序开始执行的地方
/* 这是注释 */
/* C++ 注释也可以
* 跨行
*/
#if 0
code
#endif
int main()
{
cout << "Hello World"; // 输出 Hello World
return 0;
}
- C++ 语言定义了一些头文件,这些头文件包含了程序中必需的或有用的信息。上面这段程序中,包含了头文件
。 - 下一行 using namespace std; 告诉编译器使用 std 命名空间。命名空间是 C++ 中一个相对新的概念。
- 下一行 // main() 是程序开始执行的地方 是一个单行注释。单行注释以 // 开头,在行末结束。
- 下一行 int main() 是主函数,程序从这里开始执行。
- 下一行 cout << “Hello World”; 会在屏幕上显示消息 “Hello World”。
- 下一行 return 0; 终止 main( )函数,并向调用进程返回值 0。
- 分号是语句结束符。也就是说,每个语句必须以分号结束。它表明一个逻辑实体的结束。
- 语句块是一组使用大括号括起来的按逻辑连接的语句。
基本数据类型
七种基本的 C++ 数据类型
类型 | 关键字 | 位 | 描述 |
---|---|---|---|
布尔型 | bool | 4个字节 | 存储值 true 或 false。 |
字符型 | char | 1个字节 | |
整型 | int | 4个字节 | |
浮点型 | float | 4个字节 | 单精度浮点值。单精度是这样的格式,1位符号,8位指数,23位小数。![]() |
双浮点型 | double | 8个字节 | 双精度浮点值。双精度是1位符号,11位指数,52位小数。![]() |
无类型 | void | 表示类型的缺失。 | |
宽字符型 | wchar_t | 2或4个字节 | 宽字符类型。 |
#include<iostream>
#include <limits>
using namespace std;
int main()
{
cout << "type: \t\t" << "************size**************"<< endl;
cout << "bool: \t\t" << "所占字节数:" << sizeof(bool);
cout << "\t最大值:" << (numeric_limits<bool>::max)();
cout << "\t\t最小值:" << (numeric_limits<bool>::min)() << endl;
cout << "char: \t\t" << "所占字节数:" << sizeof(char);
cout << "\t最大值:" << (numeric_limits<char>::max)();
cout << "\t\t最小值:" << (numeric_limits<char>::min)() << endl;
cout << "signed char: \t" << "所占字节数:" << sizeof(signed char);
cout << "\t最大值:" << (numeric_limits<signed char>::max)();
cout << "\t\t最小值:" << (numeric_limits<signed char>::min)() << endl;
cout << "unsigned char: \t" << "所占字节数:" << sizeof(unsigned char);
cout << "\t最大值:" << (numeric_limits<unsigned char>::max)();
cout << "\t\t最小值:" << (numeric_limits<unsigned char>::min)() << endl;
cout << "wchar_t: \t" << "所占字节数:" << sizeof(wchar_t);
cout << "\t最大值:" << (numeric_limits<wchar_t>::max)();
cout << "\t\t最小值:" << (numeric_limits<wchar_t>::min)() << endl;
cout << "short: \t\t" << "所占字节数:" << sizeof(short);
cout << "\t最大值:" << (numeric_limits<short>::max)();
cout << "\t\t最小值:" << (numeric_limits<short>::min)() << endl;
cout << "int: \t\t" << "所占字节数:" << sizeof(int);
cout << "\t最大值:" << (numeric_limits<int>::max)();
cout << "\t最小值:" << (numeric_limits<int>::min)() << endl;
cout << "unsigned: \t" << "所占字节数:" << sizeof(unsigned);
cout << "\t最大值:" << (numeric_limits<unsigned>::max)();
cout << "\t最小值:" << (numeric_limits<unsigned>::min)() << endl;
cout << "long: \t\t" << "所占字节数:" << sizeof(long);
cout << "\t最大值:" << (numeric_limits<long>::max)();
cout << "\t最小值:" << (numeric_limits<long>::min)() << endl;
cout << "unsigned long: \t" << "所占字节数:" << sizeof(unsigned long);
cout << "\t最大值:" << (numeric_limits<unsigned long>::max)();
cout << "\t最小值:" << (numeric_limits<unsigned long>::min)() << endl;
cout << "double: \t" << "所占字节数:" << sizeof(double);
cout << "\t最大值:" << (numeric_limits<double>::max)();
cout << "\t最小值:" << (numeric_limits<double>::min)() << endl;
cout << "long double: \t" << "所占字节数:" << sizeof(long double);
cout << "\t最大值:" << (numeric_limits<long double>::max)();
cout << "\t最小值:" << (numeric_limits<long double>::min)() << endl;
cout << "float: \t\t" << "所占字节数:" << sizeof(float);
cout << "\t最大值:" << (numeric_limits<float>::max)();
cout << "\t最小值:" << (numeric_limits<float>::min)() << endl;
cout << "size_t: \t" << "所占字节数:" << sizeof(size_t);
cout << "\t最大值:" << (numeric_limits<size_t>::max)();
cout << "\t最小值:" << (numeric_limits<size_t>::min)() << endl;
cout << "string: \t" << "所占字节数:" << sizeof(string) << endl;
// << "\t最大值:" << (numeric_limits<string>::max)() << "\t最小值:" << (numeric_limits<string>::min)() << endl;
cout << "type: \t\t" << "************size**************"<< endl;
return 0;
}
- endl: 将在每一行后插入一个换行符
- << 运算符: 向屏幕传多个值
- sizeof() : 获取各种数据类型的大小
- typedef: 为一个已有的类型取一个新的名字.语法是
typedef``type newname;
枚举
枚举类型(enumeration)是一种派生数据类型,是由用户定义的若干枚举常量的集合。
enum 枚举名{
标识符[=整型常数],
标识符[=整型常数],
...
标识符[=整型常数]
} 枚举变量;
如果枚举没有初始化, 即省掉”=整型常数”时, 则从第一个标识符开始。默认情况下,每个名称都会比它前面一个名称大 1.第一个名称的值为 0
enum color { red, green=5, blue } c;
c = blue;
指针
数组
引用
数据结构
类
基本语法
变量定义
type variable_name = value; // 声明并初始化变量
不带初始化的定义:带有静态存储持续时间的变量会被隐式初始化为 NULL(所有字节的值都是 0),其他所有变量的初始值是未定义的。
extern int d = 3, f = 5; // d 和 f 的声明
int d = 3, f = 5; // 定义并初始化 d 和 f
byte z = 22; // 定义并初始化 z
char x = 'x'; // 变量 x 的值为 'x'
#include <iostream>
using namespace std;
// 变量声明
extern int a, b;
extern int c;
extern float f;
int main ()
{
// 变量定义
int a, b;
int c;
float f;
// 实际初始化
a = 10;
b = 20;
c = a + b;
cout << c << endl ;
f = 70.0/3.0;
cout << f << endl ;
return 0;
}
// 函数声明
int func();
int main()
{
// 函数调用
int i = func();
}
// 函数定义
int func()
{
return 0;
}
变量作用域
有三个地方可以定义变量:
- 局部变量:在函数或一个代码块内部声明的变量。局部变量和全局变量的名称可以相同,但在函数内会覆盖全局变量的值。
- 形式参数:在函数参数的定义中声明的变量
- 全局变量:在所有函数外部声明的变量。值在程序的整个生命周期内都是有效的。可以被任何函数访问。
```cpp
include
using namespace std;
// 全局变量声明 int g = 20;
int main () { // 局部变量声明 int g = 10;
cout << g; // 10
return 0; }
当局部变量被定义时,系统不会对其初始化,您必须自行对其初始化。定义全局变量时,系统会自动初始化为下列值:
| **数据类型** | **初始化默认值** |
| --- | --- |
| int | 0 |
| char | '\\0' |
| float | 0 |
| double | 0 |
| pointer | NULL |
<a name="KFS7b"></a>
### 常量定义
常量是固定值,在程序执行期间不会改变。又叫做**字面量。**
- 整数常量:
- **十六进制**: 0x 或 0X 作为前缀
- **八进制**: 0 作为前缀
- **十进制**: 不带前缀
- **无符号整数**:U作为后缀
- **长整数(long)**: L作为后缀
- 浮点常量: 由整数部分、小数点、小数部分和指数部分组成
- 布尔常量:
- **true** 值代表真。1
- **false** 值代表假。0
- 字符常量:
- 宽字符常量:必须存储在 **wchar_t** 类型的变量中
- 窄字符常量:可以存储在 **char** 类型的简单变量中
```bash
85 // 十进制
0213 // 八进制
0x4b // 十六进制
30 // 整数
30u // 无符号整数
30l // 长整数
30ul // 无符号长整数
3.14159 // 小数形式
314159E-5L // 指数形式
L'x' // 宽字符常量
'x' // 窄字符常量
转义序列码:
转义序列 | 含义 |
---|---|
\\ | \ 字符 |
\‘ | ‘ 字符 |
\“ | “ 字符 |
\? | ? 字符 |
\a | 警报铃声 |
\b | 退格键 |
\f | 换页符 |
\n | 换行符 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ooo | 一到三位的八进制数 |
\xhh . . . | 一个或多个数字的十六进制数 |
#define identifier value // 使用 #define 预处理器定义常量
const type variable = value; // 使用 const 前缀声明指定类型的常量
#include <iostream>
using namespace std;
#define LENGTH 10
#define WIDTH 5
#define NEWLINE '\n'
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;
}
修饰符类型
允许在 char、int 和 double 数据类型前放置修饰符。修饰符用于改变基本类型的含义。
修饰符 | 整型int | 字符型char | 双精度型double |
---|---|---|---|
signed(有符号) | - [x] |
| [x] | | | unsigned(无符号) | [x] | [x] | | | long | [x] | | [x] | | short | [x] | | |
类型限定符:
const | const 类型的对象在程序执行期间不能被修改改变。 |
---|---|
volatile | 修饰符 volatile 告诉编译器不需要优化volatile声明的变量,让程序可以直接从内存中读取变量。对于一般的变量编译器会对变量进行优化,将内存中的变量值放在寄存器中以加快读写效率。 |
restrict | 由 restrict 修饰的指针是唯一一种访问它所指向的对象的方式。只有 C99 增加了新的类型限定符 restrict。 |
存储类
定义变量/函数的范围(可见性)和生命周期。
- auto(废): 声明变量时根据初始化表达式自动推断该变量的类型、声明函数时函数返回值的占位符。
- register(废):定义存储在寄存器中而不是 RAM 中的局部变量。即变量的最大尺寸等于寄存器的大小。寄存器只用于需要快速访问的变量,比如计数器
- static:编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。当 static 用在类数据成员上时,会导致仅有一个该成员的副本被类的所有对象共享。
- extern:提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。通常用于当有两个或多个文件共享相同的全局变量或函数的时候。
- mutable:仅适用于类的对象。允许对象的成员替代常量,即mutable 成员可以通过 const 成员函数修改。
- thread_local:仅应用于数据声明和定义不能用于函数声明或定义。变量仅可在它在其上创建的线程上访问
运算符
- 算术运算符:+-*/%++—
- 关系运算符:== != > < >= <=
- 逻辑运算符:&& || !
- 位运算符:&|^~<<>>
- 杂项运算符: | 杂项运算符 | 描述 | | —- | —- | | sizeof | sizeof 运算符返回变量的大小。例如,sizeof(a) 将返回 4,其中 a 是整数。 | | Condition ? X : Y | 条件运算符。如果 Condition 为真 ? 则值为 X : 否则值为 Y。 | | , | 逗号运算符会顺序执行一系列运算。整个逗号表达式的值是以逗号分隔的列表中的最后一个表达式的值。 | | .(点)和 ->(箭头) | 成员运算符用于引用类、结构和共用体的成员。访问结构的成员时使用点运算符,而通过指针访问结构的成员时,则使用箭头运算符。 | | Cast | 强制转换运算符把一种数据类型转换为另一种数据类型。例如,int(2.2000) 将返回 2。 | | & | 指针运算符 & 返回变量的地址。例如 &a; 将给出变量的实际地址。 | | | [指针运算符 ](https://www.runoob.com/cplusplus/cpp-pointer-operators.html) 指向一个变量。例如,*var; 将指向变量 var。 |
sizeof (data type)
Exp1 ? Exp2 : Exp3;
表达式1, 表达式2
(type) expression
#include <iostream>
using namespace std;
struct Employee {
char first_name[16];
int age;
} emp;
int main()
{
double a = 21.09399;
float b = 10.20;
int c ;
int var;
int *ptr;
int val;
c = (int) a; // 强制转换运算符
cout << "Line 1 - Value of (int)a is :" << c << endl ; // 21
c = (int) b; // 强制转换运算符
cout << "Line 2 - Value of (int)b is :" << c << endl ; // 10
strcpy(emp.first_name, "zara"); // 点运算符,访问结构的成员
strcpy(p_emp->first_name, "zara"); // 箭头运算符,通过指针访问结构的成员
// 获取 var 的地址
ptr = &var;
// 获取 ptr 的值
val = *ptr;
cout << "Value of var :" << var << endl; // 3000
cout << "Value of ptr :" << ptr << endl; // 0xbff64494
cout << "Value of val :" << val << endl; // 3000
return 0;
}
函数
形式参数
形式参数就像函数内的其他局部变量,在进入函数时被创建,退出函数时被销毁
- 传值调用:默认。把参数的实际值赋值给函数的形式参数。在这种情况下,修改函数内的形式参数对实际参数没有影响。
- 指针调用:把参数的地址赋值给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。
- 引用调用:把参数的引用赋值给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。
Lambda
Lambda 表达式把函数看作对象。Lambda 表达式可以像对象一样使用,比如可以将它们赋给变量和作为参数传递,还可以像函数一样对其求值。
[capture](parameters)->return-type{body}
[](int x, int y){ return x < y ; }
[]{ ++global_x; }
[](int x, int y) -> int { int z = x + y; return z + x; }
在Lambda表达式内可以访问当前作用域的变量,这是Lambda表达式的闭包(Closure)行为。 与JavaScript闭包不同,C++变量传递有传值和传引用的区别。可以通过前面的[]来指定:
[] // 沒有定义任何变量。使用未定义变量会引发错误。
[x, &y] // x以传值方式传入(默认),y以引用方式传入。
[&] // 任何被使用到的外部变量都隐式地以引用方式加以引用。
[=] // 任何被使用到的外部变量都隐式地以传值方式加以引用。
[&, x] // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
[=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。
[this] // 对于[=]或[&]的形式,显式传入this指针,
内置数学函数
函数 | 描述 |
---|---|
double cos(double); | 该函数返回弧度角(double 型)的余弦。 |
double sin(double); | 该函数返回弧度角(double 型)的正弦。 |
double tan(double); | 该函数返回弧度角(double 型)的正切。 |
double log(double); | 该函数返回参数的自然对数。 |
double pow(double, double); | 假设第一个参数为 x,第二个参数为 y,则该函数返回 x 的 y 次方。 |
double hypot(double, double); | 该函数返回两个参数的平方总和的平方根,也就是说,参数为一个直角三角形的两个直角边,函数会返回斜边的长度。 |
double sqrt(double); | 该函数返回参数的平方根。 |
int abs(int); | 该函数返回整数的绝对值。 |
double fabs(double); | 该函数返回任意一个浮点数的绝对值。 |
double floor(double); | 该函数返回一个小于或等于传入参数的最大整数。 |
rand() | 返回一个伪随机数,生成随机数之前必须先调用 srand() 函数。 |
#include <iostream>
#include <ctime>
#include <cstdlib>
using namespace std;
int main ()
{
int i,j;
// 设置种子
srand( (unsigned)time( NULL ) );
/* 生成 10 个随机数 */
for( i = 0; i < 10; i++ )
{
// 生成实际的随机数
j= rand();
cout <<"随机数: " << j << endl;
}
return 0;
}
数组
type arrayName [ arraySize ];
#include <iostream>
using namespace std;
#include <iomanip>
using std::setw;
int main ()
{
int n[ 10 ]; // n 是一个包含 10 个整数的数组
// 初始化数组元素
for ( int i = 0; i < 10; i++ ) {
n[ i ] = i + 100; // 设置元素 i 为 i + 100
}
cout << "Element" << setw( 13 ) << "Value" << endl;
// 输出数组中每个元素的值
for ( int j = 0; j < 10; j++ ) {
cout << setw( 7 )<< j << setw( 13 ) << n[ j ] << endl;
}
return 0;
}
- setw() :格式化输出
字符串
内置字符串函数
| 函数 | 描述 | | —- | —- | | strcpy(s1, s2); | 复制字符串 s2 到字符串 s1。 | | strcat(s1, s2); | 连接字符串 s2 到字符串 s1 的末尾。 | | strlen(s1); | 返回字符串 s1 的长度。 | | strcmp(s1, s2); | 如果 s1 和 s2 是相同的,则返回 0;如果 s1s2 则返回值大于 0 | | strchr(s1, ch); | 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。 | | strstr(s1, s2); | 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。 |
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string str1 = "Hello";
string str2 = "World";
string str3;
int len ;
// 复制 str1 到 str3
str3 = str1;
cout << "str3 : " << str3 << endl; // Hello
// 连接 str1 和 str2
str3 = str1 + str2;
cout << "str1 + str2 : " << str3 << endl; // HelloWorld
// 连接后,str3 的总长度
len = str3.size();
cout << "str3.size() : " << len << endl; // 10
return 0;
}
指针
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址,是一个代表内存地址的长的十六进制数
#include <iostream>
using namespace std;
int main ()
{
int var = 20; // 实际变量的声明
int *ip; // 指针变量的声明
ip = &var; // 在指针变量中存储 var 的地址
cout << "Value of var variable: ";
cout << var << endl; // 20
// 输出在指针变量中存储的地址
cout << "Address stored in ip variable: ";
cout << ip << endl; // 0xbfc601ac
// 访问指针中地址的值
cout << "Value of *ip variable: ";
cout << *ip << endl; // 20
return 0;
}
在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。NULL 指针是一个定义在标准库中的值为零的常量.
引用
引用变量是一个别名,它是某个已存在变量的另一个名字。
- 不存在空引用。引用必须连接到一块合法的内存。
- 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
- 引用必须在创建时被初始化。指针可以在任何时间被初始化。
日期和时间
函数 | 描述 |
---|---|
time_t time(time_t *time); | 该函数返回系统的当前日历时间,自 1970 年 1 月 1 日以来经过的秒数。如果系统没有时间,则返回 .1 |
char ctime(const time_t time); | 该返回一个表示当地时间的字符串指针,字符串形式 day month year hours:minutes:seconds year\n\0。 |
struct tm localtime(const time_t time); | 该函数返回程序执行起(一般为程序的开头),处理器时钟所使用的时间。如果时间不可用,则返回 .1。 |
clock_t clock(void); | 该函数返回程序执行起(一般为程序的开头),处理器时钟所使用的时间。如果时间不可用,则返回 .1。 |
char asctime ( const struct tm time ); | 该函数返回一个指向字符串的指针,字符串包含了 time 所指向结构中存储的信息,返回形式为:day month date hours:minutes:seconds year\n\0。 |
struct tm gmtime(const time_t time); | 该函数返回一个指向 time 的指针,time 为 tm 结构,用协调世界时(UTC)也被称为格林尼治标准时间(GMT)表示。 |
time_t mktime(struct tm *time); | 该函数返回日历时间,相当于 time 所指向结构中存储的时间。 |
double difftime ( time_t time2, time_t time1 ); | 该函数返回 time1 和 time2 之间相差的秒数。 |
size_t strftime(); | 该函数可用于格式化日期和时间为指定的格式。 |
#include <iostream>
#include <ctime>
using namespace std;
int main( )
{
// 基于当前系统的当前日期/时间
time_t now = time(0);
cout << "1970 到目前经过秒数:" << now << endl;
tm *ltm = localtime(&now);
// 输出 tm 结构的各个组成部分
cout << "年: "<< 1900 + ltm->tm_year << endl;
cout << "月: "<< 1 + ltm->tm_mon<< endl;
cout << "日: "<< ltm->tm_mday << endl;
cout << "时间: "<< ltm->tm_hour << ":";
cout << ltm->tm_min << ":";
cout << ltm->tm_sec << endl;
}
基本的输入输出
I/O库头文件
头文件 | 描述 |
---|---|
该文件定义了 cin、cout、cerr 和 clog 对象,分别对应于标准输入流、标准输出流、非缓冲标准错误流和缓冲标准错误流。使用 cerr 流来显示错误消息,而其他的日志消息则使用 clog 流来输出。 | |
该文件通过所谓的参数化的流操纵器(比如 setw 和 setprecision),来声明对执行标准化 I/O 有用的服务。 | |
该文件为用户控制的文件处理声明服务。该数据类型通常表示文件流,且同时具有 ofstream(CU) 和 ifstream(R) 两种功能,这意味着它可以创建文件,向文件写入信息,从文件读取信息。 |
void open(const char *filename, ios::openmode mode);
模式标志openmode | 描述 |
---|---|
ios::app | 追加模式。所有写入都追加到文件末尾。 |
ios::ate | 文件打开后定位到文件末尾。 |
ios::in | 打开文件用于读取。 |
ios::out | 打开文件用于写入。 |
ios::trunc | 如果该文件已经存在,其内容将在打开文件之前被截断,即把文件长度设为 0。 |
#include <fstream>
#include <iostream>
using namespace std;
int main (){
char data[100];
// 以写模式打开文件
ofstream outfile;
outfile.open("afile.dat");
cout << "Writing to the file" << endl;
cout << "Enter your name: ";
cin.getline(data, 100);
// 向文件写入用户输入的数据
outfile << data << endl;
cout << "Enter your age: ";
cin >> data;
cin.ignore();
// 再次向文件写入用户输入的数据
outfile << data << endl;
// 关闭打开的文件
outfile.close();
// 以读模式打开文件
ifstream infile;
infile.open("afile.dat");
cout << "Reading from the file" << endl;
infile >> data;
// 在屏幕上写入数据
cout << data << endl;
// 再次从文件读取数据,并显示它
infile >> data;
cout << data << endl;
// 关闭打开的文件
infile.close();
return 0;
}
结构
结构是另一种用户自定义的可用的数据类型,它允许您存储不同类型的数据项,用于表示一条记录。
struct type_name {
member_type1 member_name1;
member_type2 member_name2;
member_type3 member_name3;
} object_names;
#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( )
{
Books Book1; // 定义结构体类型 Books 的变量 Book1
Books Book2; // 定义结构体类型 Books 的变量 Book2
// Book1 详述
strcpy( Book1.title, "C++ 教程");
strcpy( Book1.author, "Runoob");
strcpy( Book1.subject, "编程语言");
Book1.book_id = 12345;
// Book2 详述
strcpy( Book2.title, "CSS 教程");
strcpy( Book2.author, "Runoob");
strcpy( Book2.subject, "前端技术");
Book2.book_id = 12346;
// 通过传 Book1 的地址来输出 Book1 信息
printBook( &Book1 );
// 通过传 Book2 的地址来输出 Book2 信息
printBook( &Book2 );
return 0;
}
// 该函数以结构指针作为参数
void printBook( struct Books *book )
{
cout << "书标题 : " << book->title <<endl;
cout << "书作者 : " << book->author <<endl;
cout << "书类目 : " << book->subject <<endl;
cout << "书 ID : " << book->book_id <<endl;
}
面向对象
类用于指定对象的形式,它包含了数据表示法和用于处理数据的方法。类中的数据和方法称为类的成员。
使用范围解析运算符 :: 定义类成员函数
#include <iostream>
using namespace std;
class Box
{
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
// 成员函数声明
double getVolume(void);
void setLength( double len );
void setBreadth( double bre );
void setHeight( double hei );
};
// 成员函数定义
double Box::getVolume(void){
return length * breadth * height;
}
void Box::setLength( double len ){
length = len;
}
void Box::setBreadth( double bre ){
breadth = bre;
}
void Box::setHeight( double hei ){
height = hei;
}
// 程序的主函数
int main( ){
Box Box1; // 声明 Box1,类型为 Box
Box Box2; // 声明 Box2,类型为 Box
double volume = 0.0; // 用于存储体积
// box 1 详述
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// box 2 详述
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// box 1 的体积
volume = Box1.getVolume();
cout << "Box1 的体积:" << volume <<endl;
// box 2 的体积
volume = Box2.getVolume();
cout << "Box2 的体积:" << volume <<endl;
return 0;
}
- public: 公有成员在程序中类的外部是可访问的。
- private: 私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类和友元函数可以访问私有成员。
- protected: 保护成员变量或函数与私有成员十分相似,但有一点不同,保护成员在派生类(即子类)中是可访问的。 | 继承方式 | 基类的 public 成员 | 基类的 protected 成员 | 基类的 private 成员 | 继承引起的访问关系变化 | | —- | —- | —- | —- | —- | | public继承 | 仍为public成员 | 仍为protected成员 | 不可见 | 基类的非私有成员在子类的访问属性不变 | | protected继承 | 变为protected成员 | 变为protected成员 | 不可见 | 基类的非私有成员都为子类的保护成员 | | private继承 | 变为private成员 | 变为private成员 | 不可见 | 基类中的非私有成员都称为子类的私有成员 |
一个类可以派生自多个类:
class derived-class: access-specifier base-class
- 函数重载:可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。当您调用一个重载函数或重载运算符时,编译器通过把您所使用的参数类型与定义中的参数类型进行比较,决定选用最合适的定义。选择最合适的重载函数或重载运算符的过程,称为重载决策。
- 多态:意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
- 虚函数:是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。
- 使用类来定义抽象数据类型(ADT)。类的内部受到保护,不会因无意的用户级错误导致对象状态受损。类实现可能随着时间的推移而发生变化,以便应对不断变化的需求,或者应对那些要求不改变用户级代码的错误报告。在设计组件时,必须保持接口独立于实现,这样,如果改变底层实现,接口也将保持不变。我们都会设置类成员状态为私有(private),除非我们真的需要将其暴露,这样才能保证良好的封装性。
- 接口描述了类的行为和功能,而不需要完成类的特定实现。C++ 接口是使用抽象类来实现的,如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类(ABC)。纯虚函数是通过在声明中使用 “= 0” 来指定的。抽象类不能被用于实例化对象,它只能作为接口使用。如果试图实例化一个抽象类的对象,会导致编译错误。
高级教程
异常
#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) {
//其他的错误
}
}
动态内存
- 栈:在函数内部声明的所有变量都将占用栈内存。
- 堆:这是程序中未使用的内存,在程序运行时可用于动态分配内存。
new 运算符为给定类型的变量在运行时分配堆内的内存,这会返回所分配的空间地址。
delete 运算符,释放之前由 new 运算符分配的内存。
#include <iostream>
using namespace std;
class Box {
public:
Box() {
cout << "调用构造函数!" <<endl;
}
~Box() {
cout << "调用析构函数!" <<endl;
}
};
int main() {
Box* myBoxArray = new Box[4];
delete [] myBoxArray; // 删除数组
return 0;
}
命名空间
#include <iostream>
using namespace std;
// 第一个命名空间
namespace first_space{
void func(){
cout << "Inside first_space" << endl;
}
}
// 第二个命名空间
namespace second_space{
void func(){
cout << "Inside second_space" << endl;
}
}
using namespace first_space;
int main () {
func(); // 调用第一个命名空间中的函数
return 0;
}
模板
模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。
泛型即只有在运行时才知道是什么类型,定义时并不知道。
#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; // 39
double f1 = 13.5;
double f2 = 20.7;
cout << "Max(f1, f2): " << Max(f1, f2) << endl; // 20.7
string s1 = "Hello";
string s2 = "World";
cout << "Max(s1, s2): " << Max(s1, s2) << endl; // World
return 0;
}
#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <stdexcept>
using namespace std;
template <class T>
class Stack {
private:
vector<T> elems; // 元素
public:
void push(T const&); // 入栈
void pop(); // 出栈
T top() const; // 返回栈顶元素
bool empty() const{ // 如果为空则返回真。
return elems.empty();
}
};
template <class T>
void Stack<T>::push (T const& elem) {
// 追加传入元素的副本
elems.push_back(elem);
}
template <class T>
void Stack<T>::pop () {
if (elems.empty()) {
throw out_of_range("Stack<>::pop(): empty stack");
}
// 删除最后一个元素
elems.pop_back();
}
template <class T>
T Stack<T>::top () const {
if (elems.empty()) {
throw out_of_range("Stack<>::top(): empty stack");
}
// 返回最后一个元素的副本
return elems.back();
}
int main() {
try {
Stack<int> intStack; // int 类型的栈
Stack<string> stringStack; // string 类型的栈
// 操作 int 类型的栈
intStack.push(7);
cout << intStack.top() <<endl;
// 操作 string 类型的栈
stringStack.push("hello");
cout << stringStack.top() << std::endl;
stringStack.pop();
stringStack.pop();
}
catch (exception const& ex) {
cerr << "Exception: " << ex.what() <<endl;
return -1;
}
}
预处理器
预处理器是一些指令,指示编译器在实际编译之前所需完成的预处理。以井号(#)开头,不会以分号(;)结尾。
- #include :把头文件包含到源文件中
- #define:创建符号常量
- #if:有选择地对部分程序源代码进行编译
- #else:
- #line:
```cpp
include
using namespace std;
define concat(a, b) a ## b
int main() { int xy = 100;
cout << concat(x, y); return 0; }
**预定义宏:**
| **宏** | **描述** |
| --- | --- |
| __LINE__ | 这会在程序编译时包含当前行号。 |
| __FILE__ | 这会在程序编译时包含当前文件名。 |
| __DATE__ | 这会包含一个形式为 month/day/year 的字符串,它表示把源文件转换为目标代码的日期。 |
| __TIME__ | 这会包含一个形式为 hour:minute:second 的字符串,它表示程序被编译的时间。 |
```cpp
#include <iostream>
using namespace std;
int main () {
cout << "Value of __LINE__ : " << __LINE__ << endl; // 6
cout << "Value of __FILE__ : " << __FILE__ << endl; // test.cpp
cout << "Value of __DATE__ : " << __DATE__ << endl; // Feb 28 2020
cout << "Value of __TIME__ : " << __TIME__ << endl; // 18:52:48
return 0;
}
信号处理
定义在 C++ 头文件
信号 | 描述 |
---|---|
SIGABRT | 程序的异常终止,如调用 abort。 |
SIGFPE | 错误的算术运算,比如除以零或导致溢出的操作。 |
SIGILL | 检测非法指令。 |
SIGINT | 程序终止(interrupt)信号。 |
SIGSEGV | 非法访问内存。 |
SIGTERM | 发送到程序的终止请求。 |
using namespace std;
void signalHandler(int signum) { cout << “Interrupt signal (“ << signum << “) received.\n”;
// 清理并关闭
// 终止程序
exit(signum);
}
int main () {
// 注册信号 SIGINT 和信号处理程序
signal(SIGINT, signalHandler);
while(1){
cout << “Going to sleep….” << endl;
sleep(1);
}
return 0;
}
<a name="3pZc5"></a>
### 多线程
- **pthread_create(): **创建一个新的线程,并让它可执行
| **参数** | **描述** |
| --- | --- |
| thread | 指向线程标识符指针。 |
| attr | 一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL。 |
| start_routine | 线程运行函数起始地址,一旦线程被创建就会执行。 |
| arg | 运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。 |
```cpp
#include <iostream>
// 必须的头文件
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
// 线程的运行函数
void* say_hello(void* args) {
cout << "Hello Runoob!" << endl;
return 0;
}
int main() {
// 定义线程的 id 变量,多个变量使用数组
pthread_t tids[NUM_THREADS];
for(int i = 0; i < NUM_THREADS; ++i) {
//参数依次是:创建的线程id,线程参数,调用的函数,传入的函数参数
int ret = pthread_create(&tids[i], NULL, say_hello, NULL);
if (ret != 0)
{
cout << "pthread_create error: error_code=" << ret << endl;
}
}
//等各个线程退出后,进程才结束,否则进程强制结束了,线程可能还没反应过来;
pthread_exit(NULL);
}
web 编程
公共网关接口(CGI):是一种用于外部网关程序与信息服务器(如 HTTP 服务器)对接的接口标准
/var/www/cgi-bin/cplusplus.cgi
#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 - 第一个 CGI 程序</title>\n";
cout << "</head>\n";
cout << "<body>\n";
cout << "<h2>Hello World! 这是我的第一个 CGI 程序</h2>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
/cgi-bin/cpp_get.cgi?first_name=ZARA&last_name=ALI
#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>使用 GET 和 POST 方法</title>\n";
cout << "</head>\n";
cout << "<body>\n";
form_iterator fi = formData.getElement("first_name");
if( !fi->isEmpty() && fi != (*formData).end()) {
cout << "名:" << **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 << "姓:" << **fi << endl;
}else{
cout << "No text entered for last name" << endl;
}
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
设置 Cookies
#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.w3cschool.cc;\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>CGI 中的 Cookies</title>\n";
cout << "</head>\n";
cout << "<body>\n";
cout << "设置 cookies" << endl;
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
获取 cookies
#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>CGI 中的 Cookies</title>\n";
cout << "</head>\n";
cout << "<body>\n";
cout << "<table border = \"0\" cellspacing = \"2\">";
// 获取环境变量
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;
}
资源
- C++ Programming Language Tutorials − C++ 编程语言教程。
- C++ Programming − 这本书涵盖了 C++ 语言编程、软件交互设计、C++ 语言的现实生活应用。
- C++ FAQ − C++ 常见问题
- Free Country − Free Country 提供了免费的 C++ 源代码和 C++ 库,这些源代码和库涵盖了压缩、存档、游戏编程、标准模板库和 GUI 编程等 C++ 编程领域。
- C and C++ Users Group − C 和 C++ 的用户团体提供了免费的涵盖各种编程领域 C++ 项目的源代码,包括 AI、动画、编译器、数据库、调试、加密、游戏、图形、GUI、语言工具、系统编程等。