C++学习笔记
胖虎

比较乱,有空再整理~

C++学习笔记

命名空间的using声明

一般用到标准输入输出流的时候,库函数属于命名空间std,用作std::coutstd::cin
或者在开头声明:using namespace std;
或者单独声明:using std::cin;using std::cout;

类型说明符auto

auto:让编译器去分析表达式所属的类型。

1
2
auto cnt = 0;
auto pi = 3.14;

类型指示符decltype

decltypedeclare type的缩写,译为声明类型。
能从表达式的类型推断出变量的类型,如:

1
decltype(sizeof(arr)) length;

基于范围的for语句(range for)

for (declaration : expression)

statement

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 将string中的字符分隔输出
for (auto c : str)
{
cout << c << “ ”;
}
cout << str; // h e l l o w o r l d

// 将string中字符编程大写
// 用上引用符 &
for (auto &c : str)
{
c = toupper(c);
}
cout << str; // HELLO WORLD

cpp中类相关

静态成员变量和静态成员函数

类中定义的静态成员、函数,为整个类所有,所有对象共享。所以可以直接通过类名访问,当然也可以通过对象直接访问。

静态成员函数只能直接访问静态变量和静态函数(因为不能实例化)。

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
#include <iostream>
#include <string>

using namespace std;

class test
{
private:
static int m_value; //定义私有类的静态成员变量
public:
test()
{
m_value++;
}
static int getValue() //定义类的静态成员函数
{
return m_value;
}
};

int test::m_value = 0; //类的静态成员变量需要在类外分配内存空间,需要单独初始化。const则在声明的同时初始化。

int main()
{
test t1;
test t2;
test t3;

cout << "test::m_value2 = " << test::getValue() << endl; //通过类名直接调用公有静态成员函数,获取对象个数
cout << "t3.getValue() = " << t3.getValue() << endl; //通过对象名调用静态成员函数获取对象个数
system("pause");
}
// 结果为3

img

静态成员函数 普通成员函数
所有对象共享 yes yes
隐含this指针 no yes
访问普通成员变量(函数) no yes
访问静态成员变量(函数) yes yes

类的继承

相应的构造函数也可以继承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Gene
{
public:
int index;
int dir;
Gene(int _index, int _dir) :index(_index), dir(_dir) {};
};
class Res :public Gene
{
public:
int station;
double st;
double pt;
Res(int _station, int _index, int _dir, double _st, double _pt) :Gene(_index, _dir), station(_station), st(_st), pt(_pt) {};
string fout()const;
};

读取CSV文件,表格型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ifstream inFile(filePath, ios::in);
string lineStr;
getline(inFile, lineStr);//跳过第一行
while (getline(inFile, lineStr))
{
// 打印整行字符串
//cout << lineStr << endl;
// 存成二维表结构
stringstream ss(lineStr);
string str;
vector<string> lineArray;
// 按照逗号分隔
while (getline(ss, str, ','))
lineArray.push_back(str);
strArray.push_back(lineArray);
}

判断字符串是否是数字

c++比较愚蠢,只能一个字符一个字符的判断

方法一:判断字符的ASCII范围

(数字的ASCII范围为48~57)

img

ascll

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool AllisNum(string str)  
{
for (int i = 0; i < str.size(); i++)
{
int tmp = (int)str[i];
if (tmp >= 48 && tmp <= 57)
{
continue;
}
else
{
return false;
}
}
return true;
}

方法二:使用C++提供的stringstream对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
stringstream sin(str);  
double d;
char c;
if(!(sin >> d))
{
/*解释:
sin>>t表示把sin转换成double的变量(其实对于int和float型的都会接收),
如果转换成功,则值为非0,如果转换不成功就返回为0
*/
return false;
}
if (sin >> c)
{
/*解释:
此部分用于检测错误输入中,数字加字符串的输入形式(例如:34.f),在上面的的部分(sin>>t)
已经接收并转换了输入的数字部分,在stringstream中相应也会把那一部分给清除,
此时接收的是.f这部分,所以条件成立,返回false
*/
return false;
}
return true;
}

方法三:使用std::isdigit来判断

有一个大坑,若字符是中文可能会报错。

1
2
3
4
5
6
7
bool isNumber(const string& str)
{
for (char const &c : str) {
if (std::isdigit(c) == 0) return false;
}
return true;
}

数字取整取小数

  • ceil()函数,向上取整,天花板函数

  • floor()函数,向下取整,地板函数

  • round()函数,四舍五入,取值不受正负号影响。

程序运行时间计时⏲

clock()有毫秒级的精度,直接上例子:

1
2
3
4
5
6
7
8
9
#include<time.h>
int main()
{
time_t startTime, endTime;
startTime = clock();
func();//程序运行
endTime = clock();
cout << "运行时间:" << (endTime - startTime) / CLOCKS_PER_SEC << " s" << endl;
}

cpp运算符重载

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
#include <iostream>

class Person
{
private:
int m_age;
public:
bool operator==(const Person& other)
{
std::cout << "call member function operator==" << std::endl;
if (this->m_age == other.m_age)
{
return true;
}
return false;
}
};

int main()
{
Person p1(10);
Person p2(10);

if (p1 == p2)
{
std::cout << "p1 is equal with p2." << std::endl;
}
}

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
class MyClass
{
public:
int a = 0;
MyClass(/* args */){};
friend ostream &operator<<(ostream &output, const MyClass my)
{
output << my.a << endl;
return output;
}
friend istream &operator>>(istream &input, MyClass &my)
{
input >> my.a;
return input;
}
};

int main()
{
MyClass my;
cout << my.a << endl;
cout << my;
cin >> my;
cout << my;
return 0;
}

cpp匿名函数

lambda函数的一般形式

cpp中的匿名函数用Lambda表达式实现,又称为lambda函数

可以这样来定义一个lambda函数

1
2
auto func = []() { std::cout << "Hello world"; };
func(); // now call the function

正常情况下,只要函数体中所有return都是同一个类型的话,编译器就会自行判断函数的返回类型。也可以显示地指定lambda函数的返回类型。这个需要用到函数返回值后置的功能,比如这个例子

1
[] () -> int { return 1; }

所以总的来说lambda函数的一般形式就是:

1
[captures] (params) -> ret {Statments;}

lambda函数的变量截取

  • [] 不截取任何变量
  • [&} 截取外部作用域中所有变量,并作为引用在函数体中使用
  • [=] 截取外部作用域中所有变量,并拷贝一份在函数体中使用
  • [=, &foo] 截取外部作用域中所有变量,并拷贝一份在函数体中使用,但是对foo变量使用引用
  • [bar] 截取bar变量并且拷贝一份在函数体重使用,同时不截取其他变量
  • [this] 截取当前类中的this指针。如果已经使用了&或者=就默认添加此选项。

lambda函数的使用

在类似find_if函数中需要人工定义一个cmp函数的情况,用lambda函数就会很方便,一行代码不用另外再显示定义一个函数。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <algorithm>
//类的列表,要按其中某个属性排序
vector<Solution> resArray;
//自定义排序函数
bool TALBP::cmp_fitness(Solution ind1, Solution ind2)
{
return ind1.fitness < ind2.fitness;
}
sort(resArray.begin(), resArray.end(), cmp_fitness);
//或用lambda函数(匿名函数)更方便
sort(resArray.begin(), resArray.end(), [](const Solution& s1, const Solution& s2) {return s1.fitness < s2.fitness; });

//同理
int target_index;
auto it = find_if(resArray.begin(), resArray.end(), [&](const Solution & s) {return s.op.index == target_index; });

for_each( v.begin(), v.end(), [] (int val)
{
cout << val;
} );

cpp中vector相关

初始化

初始化其大小

1
2
3
4
5
6
7
8
//默认初始化,size = 0
vector<int> v1;
//规定长度,以下操作等价
vector<int> v2(2);
v1.resize(2);
//规定长度并赋初值,以下操作等价
vector<int> v3(3, 4);
v1.resize(3, 4);

二维数组的初始化

1
vector<vector<int>> v6(m, vector<int>(n, 0));//m*n大小,值为0

用已有数值初始化

1
2
3
4
5
6
7
//用另一个数组初始化,以下操作等价
vector<int> v4(v1);
vector<int> v4 = v1;
//用另外一个数组的指针来初始化
int a[3] = {1, 2, 3};
vector<int> v5(a, a + 2);//数组指针
vector<int> v5(v1.begin(), v1.end() - 1);//容器指针

插入数据

单个在最前面,即第一个位置插入

1
v.insert(v.begin(),val)

两个vector合并,也用insert,即后一个在前一个的最后插入。

1
resStaList.insert(resStaList.end(), temp.begin(), temp.end());

排序

简单排序

从小到大排序比较简单,使用sort()函数既可

1
2
3
4
5
#include <algorithm>

sort(v.begin(),v.end()); //从小到大
sort(vec.rbegin(), vec.rend());//从大到小,逆向,等价于下式
reverse(arr.begin(), arr.end());

自定义排序

1
2
3
4
5
6
7
8
9
10
11
#include <algorithm>
//类的列表,要按其中某个属性排序
vector<Solution> resArray;
//自定义排序函数
bool TALBP::cmp_fitness(Solution ind1, Solution ind2)
{
return ind1.fitness < ind2.fitness;
}
sort(resArray.begin(), resArray.end(), cmp_fitness);
或用lambda函数(匿名函数)更方便
sort(resArray.begin(), resArray.end(),[](const Solution& s1, const Solution& s2) {return s1.fitness < s2.fitness; });

删除

Untitled

image-20210914210322302

查找并删除

1
2
3
auto it = find(opr_left.begin(), opr_left.end(), value);
auto it = find_if(opr_left.begin(), opr_left.end(), cmp);
opr_left.erase(it);

去重

unique函数来帮忙

查找相邻并且相同的,将之移到容器末尾

最后返回非重复的的迭代器处。

再将返回值到末尾的重复值用erase删除就可以得到去重后的结果了

1
2
auto last = std::unique(v.begin(), v.end());
v.erase(last, v.end());

正则表达式

符号 意义
^ 匹配行的开头
$ 匹配行的结尾
. 匹配任意单个字符
[…] 匹配[]中的任意一个字符
(…) 设定分组
\ 转义字符
\d 匹配数字[0-9]
\w 匹配字母[a-z],数字,下划线
\s 匹配空格
\W \w 取反
\D \d 取反
\S \s 取反
+ 前面的元素重复1次或多次
* 前面的元素重复任意次
? 前面的元素重复0次或1次
{n} 前面的元素重复n次
{n,} 前面的元素重复至少n次
{n,m} 前面的元素重复至少n次,至多m次
| 逻辑或
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<iostream>
#include<regex>

using namespace std;

//regex_match 匹配
//regex_search 查找
//regex_replace 替换

int main1()
{
regex reg("([a-zA-Z]*) ([a-zA-Z]*)$");
cmatch what; //匹配的词语检索出来
bool isit = regex_match("id admin", what, reg); //
for(int i = 0; i !=what.size(); ++i) //输出匹配信息
{
cout << what[i+1].first << "\t";
}
cout << "match" << endl;
}

有个坑,regex_match方法需要输入const char*,这时就需要类型转化

1
2
3
4
5
6
7
8
9
//string与const char*转换
string s = "string_To_const char* ";
const char *c_s=s.c_str();
//还可以直接使用string类中的data()函数,直接对const char *赋值
const char *c_ss = s.data();

//const char*转换string
const char* p = "const char* _To_string";
string y(p);

键值对,哈希表

unordered_map容器,直译过来就是”无序 map 容器”的意思。所谓“无序”,指的是 unordered_map 容器不会像 map 容器那样对存储的数据进行排序。换句话说,unordered_map 容器和 map 容器仅有一点不同,即 map 容器中存储的数据是有序的,而 unordered_map 容器中是无序的。

具体来讲,unordered_map 容器和 map 容器一样,以键值对(pair类型)的形式存储数据,存储的各个键值对的键互不相同且不允许被修改。但由于 unordered_map 容器底层采用的是哈希表存储结构,该结构本身不具有对数据的排序功能,所以此容器内部不会自行对存储的键值对进行排序。

值得一提的是,unordered_map 容器在<unordered_map>头文件中,并位于std命名空间中。因此,如果想使用该容器,代码中应包含如下语句:

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
#include <unordered_map>
using namespace std;

int main()
{
//创建空 umap 容器
unordered_map<int, int> umap;

//向 umap 容器添加新键值对
umap[6] = 77; //方式一
umap.emplace(3, 5); //方式二

//输出 umap 存储键值对的数量
cout << "umap size = " << umap.size() << endl;

//使用迭代器输出 umap 容器存储的所有键值对
for (auto iter = umap.begin(); iter != umap.end(); ++iter)
{
cout << iter->first << " " << iter->second << endl;
}

//使用forrange输出 umap 容器存储的所有键值对
for (auto &&it : umap) //方式一
{
cout << it.first << " " << it.second << endl;
}
for (const auto &[fir, sec] : umap) //方式二
{
cout << fir << " " << sec << endl;
}
return 0;
}

很简单,记住是先入后出的结构。操作时压入一个新成员,返回栈顶成员,或弹出栈顶成员。

基本用法

  1. push(): 向栈内压入一个成员;
  2. pop(): 从栈顶弹出一个成员;
  3. empty(): 如果栈为空返回true,否则返回false;
  4. top(): 返回栈顶,但不删除成员;
  5. size(): 返回栈内元素的大小;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <stack>
using namespace std;

int main()
{
stack<int> stk;
for (int i = 0; i < 50; i++)
{
stk.push(i);//入栈
}
cout << "栈的大小:" << stk.size() << endl;
while (!stk.empty())
{
cout << stk.top() << endl;//返回栈顶成员
stk.pop();//出栈
}
cout << "栈的大小:" << stk.size() << endl;
return 0;
}
  • Post title:C++学习笔记
  • Post author:胖虎
  • Create time:2021-05-08 00:00:00
  • Post link:https://leiwei.space/2021/05/08/2021-05-08-C++学习笔记/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.
 Comments