CS C++ Code

前言

笔者觉得自己C++语法太差,还停留在只有竞赛够用的垃圾水平,于是决定恶补

Fake C++
Real C++

实际上是笔者现在连OOP都看不懂

基本语法

enum 枚举

适合用来定义一组有限、离散的常量,如状态机、错误码、类型分类等

1
2
3
4
5
6
enum Level
{
LOW,
MEDIUM,
HIGH
};

默认的话,会分配给第一个变量low一个下标0,然后第二个变量就会有下标1,以此类推,比如

1
2
enum Level myLevel = MEDIUM;
cout << myLevel << endl; // 输出 1

如果想改变下标,有两种方式:

  1. 给每个变量都分配下标
1
2
3
4
5
6
enum Level
{
LOW = 10,
MEDIUM = 50,
HIGH = 100
};
  1. 给一个变量分配下标,然后接下来的会根据这个下标递增
1
2
3
4
5
6
enum Level
{
LOW = 5,
MEDIUM, // = 6
HIGH // = 7
};

一般来说,enum会配合switch使用

1
2
3
4
5
6
7
8
9
10
11
12
13
enum Level myLevel = MEDIUM;
switch(myLevel)
{
case 1:
cout << "Low Level" << endl;
break;
case 2:
cout << "Medium Level" << endl;
break;
case 3:
cout << "High Level" << endl;
break;
}

&

引用
1
2
int a = 1;
int& b = a; // b 是对 a 的引用

这个时候,ab共用一个地址,改变一个会影响另一个的数值,因为本质上是一样的,可以理解为别名

1
2
b = 2;
cout << a << endl; // 输出 2

值得一提的是,虽然三种写法int& aint &aint & a是等价的,但一般来说工程项目会使用第一个

注意

int& a, b中,只有a是引用,b还是普通的int变量,因为&只绑定到当前变量!

取地址

如题,就是取地址符号

在C++中,当一个变量被声明,会分配给其一个内存地址,&就可以取这个变量的地址,是十六进制格式

1
2
int a = 1;
cout << &a << endl; // 输出类似于0x7ffc05b39ebc的地址

*

指针

指针是存储内存地址的变量

1
2
3
4
5
int a = 1;
int* b = &a;
cout << a << endl; // 输出 1
cout << &a << endl; // 输出 0x7ffdb27a660c
cout << b << endl; // 输出 0x7ffdb27a660c

&一样,声明指针也推荐用int* a,而不是int *aint * a,并且指针的变量类型也要跟原变量相同

解引用

前面我们用指针变量存储了另一个变量的地址,我们也可以继续用*得到这个地址的变量的值,称为解引用

1
2
3
int a = 1;
int* b = &a;
cout << *b << endl; // 输出 1
修改指针的值
1
2
3
4
5
int a = 1;
int* b = &a;
*b = 2;
cout << *b << endl; // 输出 2
cout << a << endl; // 输出 2

->

通过指针访问成员,可以用在struct或者class之类的地方。

a->b 等价于 (*a).b

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct node
{
int num1, num2;
};

int main()
{
node b;
node* a = &b;
a->num1 = 1;
a->num2 = 2;
cout << a->num1 + a->num2 << endl; // 输出 3
cout << b.num1 + b.num2 << endl; // 输出 3
return 0;
}

内存管理

new

new可以分配一个新的内存地址

1
2
3
4
5
6
int* a = new int;
cout << a << endl; // 输出 0x61a8ce8b2eb0
cout << *a << endl; // 输出 0
*a = 1;
cout << a << endl; // 输出 0x61a8ce8b2eb0
cout << *a << endl; // 输出 1
delete

delete可以释放这个内存地址

1
2
3
4
5
6
7
8
9
10
11
int* a = new int;
cout << a << endl; // 输出 0x61a8ce8b2eb0
cout << *a << endl; // 输出 0
delete a;
cout << *a << endl; // 乱码,因为是被释放了

int* a = new int[1001](); // 创建一个指针和一维数组,a 一开始指向数组的第一位
a[10] = 1; // 可以直接访问
cout << a[10] << endl; // 输出 1
// a[i] 在这里会被编译为 *(a+i)
delete[] a; // 释放数组
注意

delete只能用于释放被new声明的内存,否则会出现segmentation fault

下面这个操作是错的,因为a++改变了指针a的指向,它不再指向之前被new的那个内存地址,而且新的内存地址不能被delete,因为你没有“合法”地拥有它

1
2
3
int* a = new int;
a++;
delete a; // 会报错 core dumped

Lambda函数

匿名函数,相当于可以在函数里面定义的函数(正常来讲是不行的)。

基础用法

一般用auto来声明,不然的话就要用std::function <void()>之类的,比较麻烦。

1
2
3
4
5
6
7
8
9
int main()
{
int x = 0;
auto fun = []() {
cout << "HELLO Lambda" << endl;
}; // 注意这有个分号
fun(); // 输出 HELLO Lambda
return 0;
}
参数

和正常函数一样传参。

1
2
3
4
5
6
7
8
9
int main()
{
auto add = [](int a, int b)
{
return a + b;
};
cout << add(1, 1) << endl; // 输出 2
return 0;
}
捕获

正常来讲是不能用Lambda函数之外的变量的,需要“捕获”进去。

1
2
3
4
5
6
7
8
9
10
11
int main()
{
int a = 0;
auto fun = [&a]()
{
a = 10;
};
fun();
cout << a << endl; // 输出 10
return 0;
}

有四种捕获方式

1
2
3
4
auto fun = [&a](); // 只捕获外界的a,并会改变外界a的值
auto fun = [=a](); // 只捕获并复制外界的a,不会改变外界a的值
auto fun = [&](); // 捕获外界的所有变量,并会改变外界的值
auto fun = [=](); // 捕获并复制外界的所有变量,不会改变外界的值
注意

使用捕获=的时候,会复制一遍所有变量,可能造成时间复杂度爆炸

面向对象编程

Object-Oriented Programming (OOP)

原则:Don’t Repeat Yoursef (DRY),即一段代码就写一遍,不重复,便于维护和使用。

Class 类

创建一个对象,它既包含数据,也包含函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class myClass
{
public:
int num1, num2; // 数据
int add(int a, int b) // 函数
{
cout << num1 << endl; // 能输出 num1
return a + b;
}
};

int main()
{
// A + B Problem
myClass test;
cin >> test.num1 >> test.num2;
cout << test.add(test.num1, test.num2) << endl;
return 0;
}
Class Constructor 构造器

Constructor是一种特殊的函数,会在类被创建的时候自动调用,名称要和Class名称相同,不返回任何东西。可以简单理解为初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class myClass
{
public:
int num1, num2;
myClass(int a, int b)
{
cout << "Hello myClass!" << endl;
num1 = a, num2 = b;
}
};

int main()
{
myClass test(1, 2);
// 创建的时候会输出 Hello myClass! 并给num1和num2赋值
cout << test.num1 << endl; // 输出 1
cout << test.num2 << endl; // 输出 2
return 0;
}
Access Specifier 访问控制符

在Class中有三种访问控制符:

  1. public 在Class以外的都能访问
  2. private 只有Class以内的能访问
  3. protected 在Class以外的只能通过继承类来访问

这样做的意义是为了封装某些数据和函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class myClass
{
public:
myClass(int a, int b) // 创建类的时候先赋值
{
num1 = a, num2 = b;
}
int add()
{
return num1 + num2;
// 这里是可以用private变量的
}
private:
int num1, num2;
int num3; // 不声明的话默认是private
};

int main()
{
myClass test(1, 2);
cout << test.add() << endl; // 输出 3
return 0;
}
Inheritance 继承

可以理解为将这个类里的数据和函数继承到另一个类。

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
class myClass
{
public:
int num1;
};

class anotherClass: public myClass // 继承
{
public:
int num2;
};

class otherClass: public anotherClass // 多层继承
{
public:
int num3;
};

class hisClass
{
public:
int hisnum;
};

class herClass: public myClass, public hisClass // 多重继承
{
public:
int hernum;
};

int main()
{
otherClass test;
test.num1 = 1, test.num2 = 2, test.num3 = 3;
cout << test.num1 + test.num2 + test.num3 << endl; // 输出 6
herClass a;
a.hisnum = 1, a.hernum = 2;
cout << a.hisnum + a.hernum << endl; // 输出 3
return 0;
}

可以继承的访问控制符 protected

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class myClass
{
protected: // 访问控制符
int num1;
};

class hisClass: public myClass
{
public:
int num2;
int add()
{
num1 = 1; // 继承过来的num1
return num1 + num2;
}
};

int main()
{
hisClass test;
test.num2 = 2; // 在这里是用不了test.num1的
cout << test.add() << endl; // 输出 3
return 0;
}

Template 模板

模板可以写一个函数或者类,在不同的数据类型都适用。

一个Template只对后面跟着的那一个函数或类生效。

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
template <typename T>
T add(T a, T b)
{
return a + b;
}

template <typename T1, typename T2> // 可以有多个typename
class myClass
{
public:
T1 val1;
T2 val2;
myClass(T1 v)
{
val1 = v;
val2 = (T2)val1/10;
}
void printVal()
{
cout << val1 << " " << val2 << endl;
}
};

int main()
{
cout << add<int>(1, 2) << endl; // 输出 3
cout << add<double>(1.2, 1.3) << endl; // 输出 2.5

myClass<int, double> a(1);
a.printVal(); // 输出 1 0.1
return 0;
}

C++项目

头文件.h

头文件里面一般写函数和类的声明、常量和类型定义、模板函数和类的完整实现。

examtest.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef EXAMTEST_H // If not defined 头文件保护
#define EXAMTEST_H

#define MAXN 100010
typedef long long LL;

class myClass
{
public:
myClass(int v);
void fun();
private:
int num;
};

template <typename T>
T add(T a, T b)
{
return a + b;
}

#endif

examtest.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <bits/stdc++.h>
#include "examtest.h"
// 项目里一般不用 using namespace std;

myClass::myClass(int v)
{
num = v;
}

void myClass::fun()
{
std::cout << num << std::endl;
}

int main()
{
LL a = 1, b = 2;
std::cout << add(a, b) << std::endl; // 输出 3
myClass test(10);
test.fun(); // 输出 10
return 0;
}

主函数参数

用于在命令行里传参给主函数,argc表示参数的数量,argv就是参数数组,其中,argv[0]是可执行文件的名字它本身。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <bits/stdc++.h>

int main(int argc, char* argv[])
{
std::cout << argc << std::endl;
for(int x = 0; x < argc; x++) std::cout << argv[x] << " ";
std::cout << std::endl;
return 0;
}
/*
如果执行的时候在命令行写 ./examtest 1 2
会输出
3
./examtest 1 2
*/

CMake


本站由 Sky Zhou 使用 Stellar 1.29.1 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
© Copyright skyzhou.top
All Rights Reserved