C++ 总结

C++ 基础

C++ 是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化编程、面向对象编程和泛型编程
C++ 是 C 的一个超集,事实上,任何合法的 C 程序都是合法的 C++ 程序。

发布时间 文档 通称 备注
2015 ISO/IEC TS 19570:2015 - 用于并行计算的扩展
2015 ISO/IEC TS 18822:2015 - 文件系统
2014 ISO/IEC 14882:2014 C++14 第四个C++标准
2011 ISO/IEC TR 24733:2011 - 十进制浮点数扩展
2011 ISO/IEC 14882:2011 C++11 第三个C++标准
2010 ISO/IEC TR 29124:2010 - 数学函数扩展
2007 ISO/IEC TR 19768:2007 C++TR1 C++技术报告:库扩展
2006 ISO/IEC TR 18015:2006 - C++性能技术报告
2003 ISO/IEC 14882:2003 C++03 第二个C++标准
1998 ISO/IEC 14882:1998 C++98 第一个C++标准

类型

类型 关键字
布尔型 bool
字符型 char
整型 int
浮点型 float
双浮点型 double
无类型 void
宽字符型 wchar_t
类型 范围
char 1 个字节 -128 到 127 或者 0 到 255
unsigned char 1 个字节 0 到 255
signed char 1 个字节 -128 到 127
int 4 个字节 -2147483648 到 2147483647
unsigned int 4 个字节 0 到 4294967295
signed int 4 个字节 -2147483648 到 2147483647
short int 2 个字节 -32768 到 32767
unsigned short int 2 个字节 0 到 65,535
signed short int 2 个字节 -32768 到 32767
long int 8 个字节 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
signed long int 8 个字节 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
unsigned long int 8 个字节 0 to 18,446,744,073,709,551,615
float 4 个字节 +/- 3.4e +/- 38 (~7 个数字)
double 8 个字节 +/- 1.7e +/- 308 (~15 个数字)
long double 8 个字节 +/- 1.7e +/- 308 (~15 个数字)
wchar_t 2 或 4 个字节 个宽字符

typedef

使用 typedef 为一个已有的类型取一个新的名字。

1
typedef int feet;

枚举

1
2
3
enum color { red=3, green, blue } c;
color cc = blue;
cout << cc << endl;

变量

可以使用 extern 关键字在任何地方声明一个变量

1
2
extern int d = 3, f = 5;    // d 和 f 的声明 
int d = 3, f = 5; // 定义并初始化 d 和 f

常量

  • 使用 #define 预处理器
  • 使用 const 关键字
1
2
3
#define LENGTH 10 

const int LENGTH = 10;

数组

1
2
3
4
5
6
7
8
9
10
11
12
double balance[10];
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;
double salary = balance[9];

// 多维数组
int a[3][4] = {
{0, 1, 2, 3} , /* 初始化索引号为 0 的行 */
{4, 5, 6, 7} , /* 初始化索引号为 1 的行 */
{8, 9, 10, 11} /* 初始化索引号为 2 的行 */
};

判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// if else
// 局部变量声明
int a = 100;

// 检查布尔条件
if( a < 20 )
{
// 如果条件为真,则输出下面的语句
cout << "a 小于 20" << endl;
}
else
{
// 如果条件为假,则输出下面的语句
cout << "a 大于 20" << endl;
}

// switch
// 局部变量声明
char grade = 'D';

switch(grade)
{
case 'A' :
cout << "很棒!" << endl;
break;
case 'B' :
case 'C' :
cout << "做得好" << endl;
break;
default :
cout << "无效的成绩" << endl;
}
cout << "您的成绩是 " << grade << endl;

循环

  • break 终止循环,且程序流将继续执行紧接着循环的下一条语句。

  • break 用于终止 switch 语句中的一个 case。

  • continue 会跳过当前循环中的代码,强迫开始下一次循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// while 循环执行
int a = 10;
while( a < 20 )
{
cout << "a 的值:" << a << endl;
a++;
}

// do 循环执行
do
{
cout << "a 的值:" << a << endl;
a = a + 1;
}while( a < 20 );

// for 循环执行
for( int a = 10; a < 20; a = a + 1 )
{
cout << "a 的值:" << a << endl;
}

运算符

算术运算符:+ - * / % ++ --

  • % 取模运算符,整除后的余数

关系运算符:== != > < >= <=
逻辑运算符:&& || !
位运算符:& | ^ ~ << >>

  • ^ 如果存在于其中一个操作数中但不同时存在于两个操作数中,二进制异或运算符复制一位到结果中
  • ~ 二进制补码运算符是一元运算符,具有”翻转”位效果,即0变成1,1变成0。
  • << 二进制左移运算符,按二进制形式把所有的数字向左移动对应的位数,高位移出(舍弃),低位的空位补零。左移n位就相当于乘以2的n次方。
  • >> 二进制右移运算符,按二进制形式把所有的数字向左移动对应的位数,高位移出(舍弃),低位的空位补零。右移n位相当于除以2的n次方。

赋值运算符:= += -= *= /= %= <<= >>= &= ^= |=

函数

函数声明会告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。

传值调用

传值调用方法,把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数不会影响实际参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 声明
int maxNum(int num1, int num2);

// 传入2个int参数,其中一个有默认值,返回 int
int maxNum(int num1, int num2)
{
// 局部变量声明
int result;

if (num1 > num2)
result = num1;
else
result = num2;

return result;
}

指针调用

指针调用方法,把参数的地址复制给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 函数定义
void swap(int *x, int *y)
{
int temp;
temp = *x; /* 保存地址 x 的值 */
*x = *y; /* 把 y 赋值给 x */
*y = temp; /* 把 x 赋值给 y */

return;
}

int main ()
{
// 局部变量声明
int a = 100;
int b = 200;

cout << "交换前,a 的值:" << a << endl;
cout << "交换前,b 的值:" << b << endl;

/* 调用函数来交换值
* &a 表示指向 a 的指针,即变量 a 的地址
* &b 表示指向 b 的指针,即变量 b 的地址
*/
swap(&a, &b);

cout << "交换后,a 的值:" << a << endl;
cout << "交换后,b 的值:" << b << endl;

return 0;
}

引用调用

引用调用方法,把参数的地址复制给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 函数定义
void swap(int &x, int &y)
{
int temp;
temp = x; /* 保存地址 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 x 赋值给 y */

return;
}

int main ()
{
// 局部变量声明
int a = 100;
int b = 200;

cout << "交换前,a 的值:" << a << endl;
cout << "交换前,b 的值:" << b << endl;

/* 调用函数来交换值 */
swap(a, b);

cout << "交换后,a 的值:" << a << endl;
cout << "交换后,b 的值:" << b << endl;

return 0;
}

Lambda 表达式

C++11 提供了对匿名函数的支持,称为 Lambda 函数(也叫 Lambda 表达式)。
Lambda 表达式把函数看作对象。Lambda 表达式可以像对象一样使用,比如可以将它们赋给变量和作为参数传递,还可以像函数一样对其求值。

在Lambda表达式内可以访问当前作用域的变量,这是Lambda表达式的闭包(Closure)行为。 与JavaScript闭包不同,C++变量传递有传值和传引用的区别。可以通过前面的[]来指定

1
2
3
4
5
6
7
8
9
/*
[] // 沒有定义任何变量。使用未定义变量会引发错误。
[x, &y] // x以传值方式传入(默认),y以引用方式传入。
[&] // 任何被使用到的外部变量都隐式地以引用方式加以引用。
[=] // 任何被使用到的外部变量都隐式地以传值方式加以引用。
[&, x] // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
[=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。
*/
[](int x, int y) -> int { int z = x + y; return z + x; }

指针 *

每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址。

指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。
通过使用一元运算符 * 来返回位于操作数所指定地址的变量的值

所有指针的值的实际数据类型,都是一样的,都是一个代表内存地址的长的十六进制数。
指针所指向的变量或常量的数据类型不同。

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

int main ()
{
int var = 20; // 实际变量的声明
int *ip; // 指针变量的声明
int *ipNull = NULL; // 空指针

ip = &var; // 在指针变量中存储 var 的地址

cout << "var 变量的地址: " << &var << endl;
cout << "var 的值: ";
cout << var << endl;

cout << "在指针变量中存储的地址: ";
cout << ip << endl;

cout << "指针中地址的值: ";
cout << *ip << endl;

return 0;
}

引用 &

  • 不存在空引用。引用必须连接到一块合法的内存。
  • 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
  • 引用必须在创建时被初始化。指针可以在任何时间被初始化。
1
2
int i = 17;
int &r = i;

输入输出

1
2
3
4
5
6
7
8
9
10
11
12
// 预定义的对象 cerr 是 ostream 类的一个实例。cerr 对象附属到标准错误设备,通常也是显示屏,但是 cerr 对象是非缓冲的,且每个流插入到 cerr 都会立即输出。
cerr << "错误 : cerr" << endl;
// 预定义的对象 clog 是 ostream 类的一个实例。clog 对象附属到标准错误设备,通常也是显示屏,但是 clog 对象是缓冲的。这意味着每个流插入到 clog 都会先存储在缓冲在,直到缓冲填满或者缓冲区刷新时才会输出。
clog << "日志 : clog " << endl;

char name[50];
cout << "请输入您的名称: ";
// 预定义的对象 cin 是 istream 类的一个实例。cin 对象附属到标准输入设备,通常是键盘。cin 是与流提取运算符 >> 结合使用
cin >> name;
// 预定义的对象 cout 是 ostream 类的一个实例。cout 对象"连接"到标准输出设备,通常是显示屏。cout 是与流插入运算符 << 结合使用的
cout << "您的名称是: " << name << endl;

数据结构(结构体)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <cstring>

using namespace std;
void printBook( struct Books book ); // 传值调用
void printBook2(struct Books *book); // 指针调用

// struct 语句定义了一个包含多个成员的新的数据类型
struct Books
{
char title[50];
int book_id;
};

int main( )
{
struct Books Book1; // 声明 Book1,类型为 Book

// Book1 详述
// strcpy(s1, s2) 复制字符串 s2 到字符串 s1。
strcpy( Book1.title, "Learn C++ Programming");
Book1.book_id = 6495407;

// 传值调用 输出 Book1 信息
printBook( Book1 );
// 指针调用
printBook2(&Book1);

return 0;
}

// 传值调用
void printBook( struct Books book )
{
cout << "Book title : " << book.title <<endl;
cout << "Book id : " << book.book_id <<endl;
}
// 指针调用
void printBook2(struct Books *book){
cout << "Book title : " << book->title <<endl;
cout << "Book id : " << book->book_id <<endl;
}

面向对象

C++ 在 C 语言的基础上增加了面向对象编程

类的基本概念

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#include <iostream>
#include <cstring>

using namespace std;

/*
* 类定义是以关键字 class 开头,后跟类的名称。
* 每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。
*/
class Box{
// 默认情况下,类的所有成员都是私有的,私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类和友元函数可以访问私有成员。
private:
double *ptr; // 指针
double length; // 长度 私有
// 保护成员变量或函数与私有成员十分相似,但有一点不同,保护成员在派生类(即子类)中是可访问的。
protected:
double breadth; // 宽度 受保护
// 关键字 public 确定了类成员的访问属性。在类对象作用域内,公共成员在类的外部是可访问的。
public:
Box(); // 构造函数
Box(double len); // 构造函数
Box(const Box &obj); // 拷贝构造函数 obj是一个对象引用,用于初始化另一个对象
~Box(); // 析构函数

friend void printLength(Box box); //友元函数

/*
* 静态成员
* 不能把静态成员放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化,
* 无论创建多少个类的对象,静态成员都只有一个副本。静态成员在类的所有对象中是共享的
*/
static int objectCount;
double height; // 高度
//成员函数可以定义在类定义内部,或者单独使用范围解析运算符 :: 来定义
void setLength(double len){
length = len;
}
double getLength(){
cout << "*ptr=len=" << *ptr << endl;
cout << "length=" << length << endl;
return length;
}

void setBreadth(double wid);
double getBreadth();
//返回体积
double getVolume(void)
{
// 每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。
cout << "getVolume 通过 this 调用 getBreadth = " << this->getBreadth() << endl;
return this->length * breadth * height;
}
};

/*
* 类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。
* 构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。
*/
Box::Box(void){
cout << "执行 构造函数..." << endl;
ptr = new double;
}
// Box::Box(double len) == Box::Box(double len): length(len)
Box::Box(double len){
cout << "执行 带参数的构造函数..." << endl;
length = len;
ptr = new double;
*ptr = len;

// 累加静态成员
objectCount += 1;
}
/*
* 拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象
* 用于:
* 1. 通过使用另一个同类型的对象来初始化新创建的对象。
* 2. 复制对象把它作为参数传递给函数。
* 3. 复制对象,并从函数返回这个对象。
*
* 拷贝构造函数 obj是一个对象引用,用于初始化另一个对象
*/
Box::Box(const Box &obj){
cout << "执行 拷贝构造函数..." << endl;
ptr = new double; // 为指针分配内存
*ptr = *obj.ptr;
}

/*
* 析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
* 析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
*/
Box::~Box(){
cout << "执行 析构函数..." << endl;
delete ptr;
}

// 成员函数可以定义在类定义内部,或者单独使用 范围解析运算符 :: 来定义
void Box::setBreadth(double wid){
breadth = wid;
}
double Box::getBreadth(){
return breadth;
}

/*
* 类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。
* 友元函数并不是成员函数。
* 友元函数没有 this 指针,因为友元不是类的成员。只有成员函数才有 this 指针。
* 友元类 = friend class ClassTwo;
*/
void printLength(Box box){
// 友元函数直接访问 私有成员
cout << "friend box.length=" << box.length << endl;
}

// 不能把静态成员放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化
int Box::objectCount = 1;
class OtherClass{
public:
/*
* C++ 内联函数是通常与类一起使用。
* 如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。
* 在类定义中的定义的函数都是内联函数,即使没有使用 inline 说明符。
*/
inline int getSquare(int num){
return num * num;
}
};

/*
* 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
* 保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
* 私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。
*/
// Box 子类
class SmallBox:public Box, public OtherClass{
public:
double getArea(){
return breadth * height;
}
};

int main( )
{
Box box1(6.0); // 声明 box1,类型为 Box,执行带参数的构造函数
box1.height = 5.0;
box1.setBreadth(7.0);
double volume = box1.height * box1.getLength() * box1.getBreadth();
cout << "box.成员 calculate volume=" << volume << endl;
cout << "box.getVolume=" << box1.getVolume() << endl;

// 使用友元函数
printLength(box1);

Box box2 = box1;
cout << "box2.getLength=" << box2.getLength() << " end."<< endl;

Box box3(1.0);// 目的是 使用构造函数累加 objectCount
// 静态成员在类的所有对象中是共享的
cout << "box.static.objectCount=" << Box::objectCount << endl;

SmallBox smallBox;
smallBox.height=2.0;
smallBox.setBreadth(3.0);
smallBox.setLength(4.0);
cout << "small box getVolume(box)=" << smallBox.getVolume() << endl;
cout << "small box getArea(self)=" << smallBox.getArea() << endl;
cout << "small box getSquare(OtherClass)=" << smallBox.getSquare(4) << endl;

// 指向类的指针
Box *ptrBox;
// 必须在使用指针之前,对指针进行初始化。
ptrBox = &box1;
cout << "ptrBox.volume" << ptrBox->getVolume() << endl;

return 0;
}

继承、多态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include <iostream>
using namespace std;

// 形状
class Shape{
protected:
double breadth;
double height;
public:
// 重载,函数同名,形式参数(指参数的个数、类型或者顺序)不同
void print(int i){
cout << "print int:" << i << endl;
}
void print(double d){
cout << "print double:" << d << endl;
}
// 重载 + 运算符
double operator+(const Shape &s){
return this->height + s.height;
}
// 重载 ++ 运算符
double operator++(){
return ++height;
}
// 使用友元函数 重载输出
friend ostream &operator<<( ostream &output, const Shape &s){
output << "height : " << s.height << " breadth : " << s.breadth;
return output;
}

void setHeight(double h);
double getHeight();
void setBreadth(double wid);

/*
* 虚函数
* 多态,子类重写该方法
* 虚函数 是在基类中使用关键字 virtual 声明的函数。
* 在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。
* 如果没有 virtual 关键字,子类始终执行 父类的 area,就是 静态多态,或静态链接 - 函数调用在程序执行前就准备好了。有时候这也被称为早绑定,
*/
virtual double area(){
cout << "执行 Shape.area" << endl;
return 0;
}
/*
* 纯虚函数(我理解就是抽象方法)
* 您可能想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,
* 但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。
*/
//virtual double area() = 0;
};

void Shape::setHeight(double h) {
height = h;
}
double Shape::getHeight() {
return height;
}
void Shape::setBreadth(double wid) {
breadth = wid;
}

// 三角形
class Triangle: public Shape{
public:
double area(){
cout << "执行 Triangle.area" << endl;
return (breadth * height)/2;
}
};

// 矩形
class Rectangle: public Shape{
public:
double area(){
cout << "执行 Rectangle.area" << endl;
return breadth * height;
}
};

int main(void){
Shape shape1;
// 重载方法
shape1.print(123);
shape1.print(1.23);
// 重载 输出运算符
cout << "重载输出:" << shape1 << endl;
shape1.setHeight(11);
shape1.setBreadth(12);

Shape shape2;
shape2.setHeight(22);

double height;
// 重载 + 运算符
height = shape1 + shape2;
cout << "重载 + 运算符:" << height << endl;
// 重载 ++ 运算符
cout << "重载 ++ 运算符:" << ++shape1 << endl;

// 多态
Shape *shape;
Triangle triangle;
Rectangle rectangle;

triangle.setBreadth(11);
triangle.setHeight(12);

rectangle.setBreadth(22);
rectangle.setHeight(23);

// 编译器看的是指针的内容,而不是它的类型。
shape = &rectangle;
shape->area();

shape = &triangle;
shape->area();
}

进阶

文件流

标准库 fstream

  • ofstream 该数据类型表示输出文件流,用于创建文件并向文件写入信息。
  • ifstream 该数据类型表示输入文件流,用于从文件读取信息。
  • fstream 该数据类型通常表示文件流,且同时具有 ofstream 和 ifstream 两种功能,这意味着它可以创建文件,向文件写入信息,从文件读取信息。

open() 成员函数的第一参数指定要打开的文件的名称和位置,第二个参数定义文件被打开的模式。

  • ios::app 追加模式。所有写入都追加到文件末尾。
  • ios::ate 文件打开后定位到文件末尾。
  • ios::in 打开文件用于读取。
  • ios::out 打开文件用于写入。
  • ios::trunc 如果该文件已经存在,其内容将在打开文件之前被截断,即把文件长度设为 0。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <fstream>

using namespace std;

int main(){
char data[100];

ofstream outfile;
// 以写模式打开文件
outfile.open("testFile.txt");

cout << "Writing to the file" << endl;
cout << "1.Enter : ";
// 获取输入内容
cin.getline(data,100);

// 向文件写入 输入的内容
outfile << data << endl;

cout << "2.Enter : ";
cin >> data;
cin.ignore();

// 向文件写入 输入的内容
outfile << data << endl;

// 关闭文件
outfile.close();

ifstream infile;
// 以读模式打开文件
infile.open("testFile.txt");

infile >> data;
cout << "Reading from the file" << endl;
cout << data << endl;

infile >> data;
cout << data << endl;

// 关闭文件
infile.close();
}

异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <exception>

using namespace std;

// 自定义异常
struct MyException: public exception{
// what() 是异常类提供的一个公共方法,它已被所有子异常类重载。这将返回异常产生的原因。
const char *what() const throw(){
return "my exception";
}
};
double division(int a,int b){
if(b==0){
// 抛出一个异常
throw "Division by zero condition";
}
return a/b;
}
int main(){
try{
division(12,0);
}catch (const char *msg){
cerr << msg << endl;
}

try {
throw MyException();
}catch(MyException &e){
std::cout << e.what() << std::endl;
}catch (std::exception &e){
std::cout << e.what() << std::endl;
}
return 0;
}

内存

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

using namespace std;

int main(){
/*
* 栈:在函数内部声明的所有变量都将占用栈内存。
* 堆:这是程序中未使用的内存,在程序运行时可用于动态分配内存。
*/

// 初始化为 null 的指针
double* num = NULL;
// 建议检查 new 运算符是否返回 NULL 指针
// 使用 new 运算符来为任意的数据类型动态分配内存
// new 与 malloc() 函数相比,new 不只是分配了内存,它还创建了对象。
if( !(num = new double)){
cout << "error out of memory";
exit(1);
}
*num = 123.456;
cout << "double is " << *num << endl;
// 释放内存
delete num;

return 0;
}

命名空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>

/*
* using 指令也可以用来指定命名空间中的特定项目。
* 例如,如果您只打算使用 std 命名空间中的 cout 部分
* std 命名空间中的其他项目仍然需要加上命名空间名称作为前缀
*/
using std::cout;

// 命名空间,区分不同库中相同名称的函数、类、变量等。命名空间就是定义了一个范围。
namespace firstSpace{
void print(){
cout << "first space print" << std::endl;
}
}
namespace secondSpace{
void print(){
cout << "second space print" << std::endl;
}
}

/*
* 使用 using namespace 指令,在使用命名空间时就可以不用在前面加上命名空间的名称。
* 这个指令会告诉编译器,后续的代码将使用指定的命名空间中的名称。
*/
using namespace firstSpace;

int main(){
// 使用 using namespace 指令,在使用命名空间时就可以不用在前面加上命名空间的名称
print();
// 调用带有命名空间的函数或变量,需要在前面加上命名空间的名称
secondSpace::print();
return 0;
}

泛型

函数

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

using namespace std;

// 函数模板
template <typename T>
inline T const& maxObj(T const& a, T const& b){
return a > b ? a:b;
}

int main(void){
cout << maxObj(12,23) << endl;
cout << maxObj("hello","world") << endl;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
#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() is empty");
}
// 删除最后一个元素
elems.pop_back();
}

template <class T>
T Stack<T>::top() const const{
if (elems.empty()){
throw out_of_range("Stack<>::pop() is empty");
}
// 返回最后一个元素
return elems.back();
}



int main(void){
try{
Stack<int> intStack;
Stack<string> stringStack;

intStack.push(4);
cout << intStack.top() << endl;

stringStack.push("hello");
cout << stringStack.top() << std::endl;

stringStack.pop();
stringStack.pop();
}catch (exception const& ex){
cerr << "exception info = " << ex.what() << endl;
return -1;
}
return 0;
}

预处理

预处理器是一些指令,指示编译器在实际编译之前所需完成的预处理。
所有的预处理器指令都是以井号(#)开头,只有空格字符可以出现在预处理指令之前。预处理指令不是 C++ 语句,所以它们不会以分号(;)结尾。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>

using namespace std;

// #define 预处理指令用于创建符号常量。该符号常量通常称为宏
#define DEBUG true
// 定义一个带有参数的宏
#define MIN(a,b) ((a<b)?a: b)
// # 和 ## 预处理运算符在 C++ 和 ANSI/ISO C 中都是可用的。# 运算符会把 replacement-text 令牌转换为用引号引起来的字符串。
#define MKSTR(x) #x

// ## 运算符用于连接两个令牌。
#define CONCAT(x, y) x ## y

int main(void){

//条件编译
#ifdef DEBUG
cerr << "current is debug" << endl;
#endif

// 可以使用 #if 0 语句注释掉程序的一部分
#if 0
cout << "不进行编译的代码" << endl;
#endif

// 带参数的宏
cout << MIN(22,11) << endl;

// # 运算符会把 replacement-text 令牌转换为用引号引起来的字符串。
cout << MKSTR(HELLO C++) << endl;

// ## 运算符用于连接两个令牌。
cout << CONCAT(11, 22) << endl;

// 预定义宏
cout << "当前行号:" << __LINE__ << endl;
cout << "当前文件名:" << __FILE__ << endl;
cout << "当前日期:" << __DATE__ << endl;
cout << "当前时间:" << __TIME__ << endl;

return 0;
}

信号处理(我的理解是swift中的通知)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <csignal>

using namespace std;

void signalHandler( int signum ) {
cout << "Interrupt signal (" << signum << ") received.\n";
exit(signum);
}

int main(void){
/*
* 以下 列信号可以在程序中捕获,并可以基于信号采取适当的动作。这些信号是定义在 C++ 头文件 <csignal> 中。
* SIGABRT 程序的异常终止,如调用 abort。
* SIGFPE 错误的算术运算,比如除以零或导致溢出的操作。
* SIGILL 检测非法指令。
* SIGINT 接收到交互注意信号。
* SIGSEGV 非法访问内存。
* SIGTERM 发送到程序的终止请求。
*/

/*
* signal 函数,用来捕获突发事件
* 第一个参数是一个整数,代表了信号的编号;
* 第二个参数是一个指向信号处理函数的指针。
*/
signal(SIGINT,signalHandler);

int i = 0;
while(++i){
if (i == 3){
// 使用函数 raise() 生成信号
raise(SIGINT);
}
}
return 0;
}

多线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <iostream>
#include <pthread.h>

using namespace std;

#define NUM_THREADS 5

struct ThreadData{
int threadId;
char *message;
};

void* sayHello(void *args){
// 对传入的参数进行强制类型转换
struct ThreadData *threadData;
threadData = (struct ThreadData*) args;

cout << "hello threadId = " << threadData->threadId ;
cout << " message = " << threadData->message << endl;

// 在线程完成工作后无需继续存在时 调用pthread_exit退出线程
pthread_exit(NULL);
}

int main(void){

// 线程的 id 变量,多个变量使用数组
pthread_t threads[NUM_THREADS];
struct ThreadData threadData[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i++){
cout << "for thread create i = " << i << endl;
threadData[i].threadId = i;
threadData[i].message = "this is thread message";

/*
* pthread_create 创建一个新的线程,并让它可执行
*
* thread 指向线程标识符指针。
* attr 一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL。
* start_routine 线程运行函数起始地址,一旦线程被创建就会执行。
* arg 运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。
*
* 通过结构传递多个参数。您可以在线程回调中传递任意的数据类型,因为它指向 void
*/
int thread = pthread_create(&threads[i], NULL, sayHello, (void *)&(threadData[i]));
if (thread != 0 ){
cout << "thread create error: " << thread << endl;
exit(-1);
}
}
/*
* pthread_exit 用于显式地退出一个线程。
* 通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。
*/
pthread_exit(NULL);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <pthread.h>
#include <unistd.h>

using namespace std;

#define NUM_THREADS 5

void* sayHello(void *args){
sleep(1);
// 对传入的参数进行强制类型转换
int threadId = *((int *)args);
cout << "hello thread id = " << threadId << endl;

// 在线程完成工作后无需继续存在时 调用pthread_exit退出线程
pthread_exit(NULL);
}

int main(void){

pthread_t threads[NUM_THREADS];
pthread_attr_t attr;
void *status;

// 初始化并设置线程为可连接的(joinable)
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

for (int i = 0; i< NUM_THREADS; i++){
cout << "for create thread = " << i << endl;
int thread = pthread_create(&threads[i], NULL, sayHello, (void *)&i);
if (thread){
cout << "for create thread error = " << thread << endl;
exit(-1);
}
}

// 删除属性
pthread_attr_destroy(&attr);
for (int i = 0 ; i< NUM_THREADS; i++){
/*
* pthread_join() 子程序阻碍调用程序,直到指定的 threadid 线程终止为止。
* 当创建一个线程时,它的某个属性会定义它是否是可连接的(joinable)或可分离的(detached)。
* 只有创建时定义为可连接的线程才可以被连接。如果线程创建时被定义为可分离的,则它永远也不能被连接。
* 使用 pthread_join() 函数来等待线程的完成
*/
int thread = pthread_join(threads[i], &status);
if (thread){
cout << "error unable join = " << thread << endl;
exit(-1);
}
cout << "for completed thread = " << i;
cout << " exiting with status = " << status << endl;
}
cout << "main completed" << endl;
return 0;
}

CGI

未整理

标准库

C++ 标准库包含了所有的 C 标准库,为了支持类型安全,做了一定的添加和修改。

标准函数库

这个库是由通用的、独立的、不属于任何类的函数组成的。函数库继承自 C 语言。

  • 输入/输出 I/O
  • 字符串和字符处理
  • 数学
  • 时间、日期和本地化
  • 动态分配
  • 其他
  • 宽字符函数

面向对象类库

这个库是类及其相关函数的集合。

  • 标准的 C++ I/O 类
  • String 类
  • 数值类
  • STL 容器类
  • STL 算法
  • STL 函数对象
  • STL 迭代器
  • STL 分配器
  • 本地化库
  • 异常处理类
  • 杂项支持库

个人补充

->.

-> 前面是指针,. 前面是变量

域运算符 ::

  • 不能把静态成员放置在类的定义中,但是可以在类的外部通过使用 范围解析运算符 :: 来重新声明静态变量从而对它进行初始化,
  • 成员函数可以定义在类定义内部,或者在类外单独使用 范围解析运算符 :: 来定义
  • 如果有个局部变量与全局变量同名 ::a 可以使用全局变量
  • :: 只能访问静态成员函数
  • std::cout 表示 cout 是在 std 中的变量
  • using std::cout 只使用 std 命名空间中的 cout 部分 std 命名空间中的其他项目仍然需要加上命名空间名称作为前缀,如 std::endl
  • :: 左边一定是类名/命名空间,右边一定是变量名/函数名,左侧没有东西的时候,代表右侧的函数/变量是全局的

*指针 和 &引用

  • & 在定义变量的时候是引用,起标识作用,如 int &a = b; a为b的一个引用,
    引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名。
    声明一个引用,不是新定义了一个变量,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。
    不能建立数组的引用
  • & 在表达式中为取地址,你的地址可以是任何类型 如 int *a = &b; a为指向b整型的一个指针
  • * 是指针,是取地址的内容

include ""include <>

引号会先在程序所在的路径里找(自定义的库函数)
尖括号是从系统的头文件开始找(标准库函数)

本文标题:C++ 总结

文章作者:史彦超

发布时间:2016年10月11日 - 22:10

最后更新:2021年07月20日 - 16:07

原始链接:https://doingself.github.io/2016/10/11/2016-10-11-C++%E6%80%BB%E7%BB%93/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Donate comment here