C++ 备忘录

[TOC]

相关书籍

入门 C++ Primer Plus
C++ Primer
提高 深度探索C++对象模型
More Effective C++ 35
Effective C++ 55
Effective Modern C++
进阶篇 STL 源码剖析
C++标准库
GOF23 设计模式 C++
C++并发编程实战
C++性能优化指南
网络编程 Linux多线程-服务端编程-
使用muduo c++ 网络库
Unix网络编程 三件套
TCP/UP协议 三卷

APIs

标准库

1.int/float to string/array:

C语言提供了几个标准库函数,可以将任意类型(整型、长整型、浮点型等)的数字转换为字符串,下面列举了各函数的方法及其说明。
● itoa():将整型值转换为字符串
● ltoa():将长整型值转换为字符串。
● ultoa():将无符号长整型值转换为字符串。
● gcvt():将浮点型数转换为字符串,取四舍五入。
● ecvt():将双精度浮点型值转换为字符串,转换结果中不包含十进制小数点。
● fcvt():指定位数为转换精度,其余同ecvt()。

除此外,还可以使用sprintf系列函数把数字转换成字符串,其比itoa()系列函数运行速度慢

2. string/array to int/float

C/C++语言提供了几个标准库函数,可以将字符串转换为任意类型(整型、长整型、浮点型等)。

● atof():将字符串转换为双精度浮点型值。
● atoi():将字符串转换为整型值。
● atol():将字符串转换为长整型值。
● strtod():将字符串转换为双精度浮点型值,并报告不能被转换的所有剩余数字。
● strtol():将字符串转换为长整值,并报告不能被转换的所有剩余数字。
● strtoul():将字符串转换为无符号长整型值,并报告不能被转换的所有剩余数字。

数据类型&变量&常量

int to string

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 1.
int a = 10;
char *intStr = itoa(a);
string str = string(intStr);

//2
stringstream ss;
ss << a;
string str = ss.str();

//3 c++11
#include <string>
std::string s = std::to_string(42);

//4 C++ 98
#include <sstream>
#define SSTR(x) static_cost<std::ostringstream &> ((std::ostringstream() << std::dec << x)).str()

//5 Boost
#include <boost/lexical_cast.hpp>
int num = 4;
std::string str = boost::lexical_cast<std::string>(num);

rand

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdlib.h>

int N = rand() % 11; // 0~10的随机数
int N = rand() % 10; // 0~9的随机数
int N = 1 + rand() % 10; // 1-10
a + rand() % n


if1==rand()%10
//10%的概率达成,这里编辑余下代码
else
//90%的概率没达成,这里编辑余下代码

修饰符

unsinge

存储类

  • auto

  • register

  • static 全局变量

  • extern 全局变量的引用

  • mutable

  • thread_local (C++11)

  • const (只读变量)

  • constptr (常量)

    • C++ 11标准中,为了解决 const 关键字的双重语义问题,保留了 const 表示“只读”的语义,而将“常量”的语义划分给了新添加的 constexpr 关键字。

    • C++11 constexpr和const的区别详解

从 C++ 17 开始,auto 关键字不再是 C++ 存储类说明符,且 register 关键字被弃用。

运算符 循环 判断

函数

1
2
3
void func(Cobject* object);
void func(Cobect& object);
void func(Cobject object);

有什么区别呢?

  1. void func(Cobject* object)

    这种方式是将类指针作为参数,函数压入参数时实际上复制了指针的值(其实指针可以看作一个存放地址值的×××),实际复制的指针仍指向原对象的空间,所以func函数对该指针的操作是对原对象空间的操作。

  2. void func(Cobject& object)

    这种方式和传指针类似,但函数压入参数时实际上复制了object对象的this指针,其实this指针不是一个真正存在的指针,可以看作是每个对象的数据空间起始地址。func函数中对this指针的操作,实际上也是对原对象空间的操作

  3. void func(Cobject object)

    这种函数是非常不建议的,因为函数参数压栈时,对object进行了复制(还记得拷贝构造函数吗),所以函数对object的操作实际上是对新的对象空间进行的操作,不会影响原对象空间。由于不必要的拷贝对象是十分浪费时间的,也没有意义,我们完全可以用下面函数代替

    1
    func(const Cobject& object);

这样也能保护对象的只读性质。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1、值传递
实参是变量,表达式等值。
find(int x){}
y= find(z);
上面的例子中,z是实参,x是形参。x变z不变。
在值传递过程中,实参和形参位于内存中两个不同地址中,实参先自己复制一次拷贝,再把拷贝复制给形参。所以,在值传递过程中,形参的变化不会对实参有任何的影响。

2、地址传递(也称引用传递)

实参是指针。
在函数调用的时候,实参传递给你的是指针地址,地址一样也就意味着实参和形参是一样的,当你的形参发生改变时,实参也会发生改变。

find(int &x){}
y= find(z);
上面的例子中,z是实参,x是形参。z随x而改变。

数字运算

cos, sin tan, log, pow,

hypot, sqrt, abs, fabs, floor

数组

[C++]指针数组以及数组初始化

1
int *pia = new int[10]; // array of 10 uninitialized ints

枚举enum

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//    定义格式:枚举类型的定义格式为:
enum <类型名> {<枚举常量表>};
enum color_set1 {RED, BLUE, WHITE, BLACK}; //定义枚举类型color_set1
enum week {Sun, Mon, Tue, Wed, Thu, Fri, Sat}; //定义枚举类型week


各枚举常量的值可以重复。例如:
enum fruit_set {apple, orange, banana=1, peach, grape}
//枚举常量apple=0,orange=1, banana=1,peach=2,grape=3。
enum week {Sun=7, Mon=1, Tue, Wed, Thu, Fri, Sat};
//枚举常量Sun,Mon,Tue,Wed,Thu,Fri,Sat的值分别为7、1、2、3、4、5、6。
枚举常量只能以标识符形式表示,而不能是整型、字符型等文字常量。例如,以下定义非法:
enum letter_set {'a','d','F','s','T'}; //枚举常量不能是字符常量
enum year_set{2000,2001,2002,2003,2004,2005}; //枚举常量不能是整型常量
可改为以下形式则定义合法:
enum letter_set {a, d, F, s, T};
enum year_set{y2000, y2001, y2002, y2003, y2004, y2005};

字符串String

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
## string -> char*
#### data()方法,如:
1 string str = "hello";
2 const char* p = str.data();//加const 或者用char * p=(char*)str.data();的形式

#### c_str()方法,如:
1 string str=“world”;
2 const char *p = str.c_str();//同上,要加const或者等号右边用char*

#### copy()方法,如:
1 string str="hmmm";
2 char p[50];
3 str.copy(p, 5, 0);//这里5代表复制几个字符,0代表复制的位置,
4 *(p+5)=‘\0’;//注意手动加结束符!!!


## 二、char* -> string
####
1 string s;
2 char *p = "hello";//直接赋值
3 s = p;

## 三、stringchar[]
1 string pp = "dagah";
2 char p[8];
3 int i;
4 for( i=0;i<pp.length();i++)
5 p[i] = pp[i];
6 p[i] = '\0';
7 printf("%s\n",p);
8 cout<<p;

string 赋值

1
2
3
4
5
6
7
8
9
10
// 方法1 (ostringstream)
#include <sstream>
ostringstream buffer;
buffer << "sss," << 2015 << "_"<< 1;
string str = buffer.str();

// char --(sprintf)--> string
char buffer[BUFFER_SIZE];
sprintf(buffer,"%s_%d_%d","cplusplus",2015,1);
string str = buffer;

string.substr(起点, length)

1
2
// string对象.substr(起点 , 切片长度)
operate_str = initial_str.substr(0, 3);

string.erase()

1
2
3
4
5
6
1.erase(pos,n);
删除从下标pos开始的n个字符,比如erase(0,1)就是删除第一个字符
2.erase(position);
删除postion处的一个字符(position是一个string类型的迭代器)
3.erase(first,last)
删除从first到last之间的字符(first和last都是迭代器)

string 对比

1
2
3
4
5
6
7
std::string h2 = "BUS_0001";
std::string h3 = "BUS_0001";
printf(" h3 == h2: %d\n",h3 == h2);

if(h3 == h2)

h3.compare(h2) == 0 // 相等

string 高级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
## 1. 逆序
string orderstr = "hello world";
cout << "正序字符串:" << orderstr << endl;
reverse(orderstr.begin(), orderstr.end());
cout << "逆序字符串:" << orderstr << endl;


## 2.将一个字符串按照空格划分成单词
string str = "hello world, how are you?";
stringstream ss(str);
string word;
while (ss >> word) {
cout << word << endl;
}

指针

引用

memcpy…apis

memset(void *str, int c, size_t n)

复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。

memcpy(void *destin, void *source, unsigned n);

从存储区 source复制n 个字节到存储区 destin。

将地址从src开始的前count个字节的内容拷贝到地址从dst开始的内存空间中。

文件读取I/O

C++文件读写详解(ofstream,ifstream,fstream)

img

1、基于控制台io

1
2
3
4
#include <iostream>
istream // 流读取 (input)
ostream // 写入流 (output)
iostram // 对流读写

2、基于文件IO [ifstream/ofstream]

1
2
3
4
5
6
#include <fstream>
ifstream //文件读操作,存储设备读区到内存中
ofstream //文件写操作 内存写入存储设备
fstream //读写操作,对打开的文件可进行读写操作

// 当 fstream/ifstream/ofstream 对象超出范围时,文件将自动关闭。

ref:

https://www.toptip.ca/2012/11/is-it-necessary-to-manually-close.html

3、基于字符串IO [i/ostringstream]

1
2
3
4
#include <sstream>
istringstream // 从string对象读取
ostringstream // 写入到string对象
stringstream // 对string对象读写

ref:

c++的输入输出 [一](istream,ostream,iostream)

c++的输入输出 [二](ifstream,ofstream,fstream)

c++的输入输出 [三](istringstream,ostringstream,stringstream)

C++文件读写详解(ofstream,ifstream,fstream)

c++多进程读取同一个文件

C Plus 第 6.1 节 带文件的输入/输出

c++多线程:互斥锁

c++输入文件流ifstream用法详解_陈 洪 伟的博客-CSDN博客_ifstream用法

时间

1、时间戳毫秒/秒

gettimeofday()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// linux
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
struct timeval time;

/* 获取时间s */
gettimeofday(&time, NULL);
//printf("s: %ld, ms: %ld\n", time.tv_sec, (time.tv_sec*1000 + time.tv_usec/1000));

/* 获取时间us, s */
struct timeval time_now{};
gettimeofday(&time_now, nullptr);
time_t msecs_time = (time_now.tv_sec * 1000) + (time_now.tv_usec / 1000);

cout << "seconds since epoch( s): " << time_now.tv_sec << endl;
cout << "milliseconds since epoch(ms): " << msecs_time << endl << endl;
}
chrono
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 <chrono>
#include <iostream>
#include <sys/time.h>
#include <ctime>

using std::cout; using std::endl;
using std::chrono::duration_cast;
using std::chrono::milliseconds;
using std::chrono::seconds;
using std::chrono::system_clock;

int main() {
auto millisec_since_epoch = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
auto sec_since_epoch = duration_cast<seconds>(system_clock::now().time_since_epoch()).count();

cout << "seconds since epoch: " << sec_since_epoch << endl;
cout << "milliseconds since epoch: " << millisec_since_epoch << endl;


auto tp = std::chrono::system_clock::now();

auto seconds = std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch());
cout << seconds.count() << " s" << endl; // seconds from 1970 1651286747

return EXIT_SUCCESS;
}

Demo

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
#include <cstdint>
#include <ctime>
#include <iostream>
#include <chrono>
#include <sstream>
#include <iomanip>

using namespace std;

void time_point_to_duration();
void duration_to_time_point();
void format_time_point();
void parse_from_string();

int main(int argc, char *argv[])
{
time_point_to_duration();

duration_to_time_point();

format_time_point();

parse_from_string();

return 0;
}

void time_point_to_duration()
{
auto tp=std::chrono::system_clock::now();

auto seconds=std::chrono::duration_cast<std::chrono::seconds>(tp.time_since_epoch());
cout<<seconds.count()<<" s"<<endl;//seconds from 1970

auto ms=std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch());
cout<<ms.count()<<" ms"<<endl;

auto us=std::chrono::duration_cast<std::chrono::microseconds>(tp.time_since_epoch());
cout<<us.count()<<" us"<<endl;

auto ns=std::chrono::duration_cast<std::chrono::nanoseconds>(tp.time_since_epoch());
cout<<ns.count()<<" ns"<<endl;

cout<<tp.time_since_epoch().count()<<" default is ns"<<endl;
}

void duration_to_time_point()
{
std::uint64_t ticker=1609756793160376465;
auto ns=std::chrono::nanoseconds(ticker);

auto tp1=std::chrono::time_point<std::chrono::system_clock,std::chrono::nanoseconds>(ns);
auto tp2=tp1-std::chrono::hours(1);//time point before one hour
cout<<"tp1="<<tp1.time_since_epoch().count()<<endl<<"tp2="<<tp2.time_since_epoch().count()<<endl;
}

void format_time_point()
{
auto tp=std::chrono::system_clock::now();
auto time=std::chrono::system_clock::to_time_t(tp);
std::stringstream ss;
ss<<std::put_time(std::localtime(&time),"%Y-%m-%d %H:%M:%S");

cout<<ss.str()<<endl;
}

void parse_from_string()
{
std::stringstream ss;
ss<<"2021-01-06 18:47:35";

std::tm tm{};
ss>>std::get_time(&tm,"%Y-%m-%d %H:%M:%S");
auto tp=std::chrono::system_clock::from_time_t(std::mktime(&tm));
cout<<tp.time_since_epoch().count()<<endl;
}

//原文链接:https://blog.csdn.net/lianshaohua/article/details/112531154
掩耳盗铃
1
2
3
4
5
time_t now = time(nullptr);
time_t mnow = now * 1000;

cout << "seconds since epoch: " << now << endl;
cout << "milliseconds since epoch: " << mnow << endl << endl;

Windows/Linux Get Millisecond

get time in millisecond

2、时间戳(s)

1
2
3
4
5
6
7
// time for int s
time_t now = time(0);
// time to string format
tm *ltm = localtime(&now);
char s[100]{};
strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", ltm);
string timestr(s);
1
2
3
4
std::time_t t = std::time(NULL);
std::tm tm = *std::localtime(&t);
std::cout.imbue(std::locale("CHS"));
std::cout << std::put_time(&tm,"%Y-%m-%d %H:%M:%S") << '\n';

3、localtime(time_t)

C 库函数 struct tm *localtime(const time_t *timer) 使用 timer 的值来填充 tm 结构。timer 的值被分解为 tm 结构,并用本地时区表示。

4、strftime()

size_t strftime(char *str, size_t count, const char *format, const struct tm *tm);

1
2
3
4
str, 表示返回的时间字符串
count, 要写入的字节的最大数量
format, 格式字符串由零个或多个转换符和普通字符(除%)
tm, 输入时间

基本的输入输出

数据结构

APIs C++ 11

auto

C++11这次的更新带来了令很多C++程序员期待已久的for range循环,每次看到javascript, lua里的for range,心想要是C++能有多好,心里别提多酸了。这次C++11不负众望,再也不用羡慕别家人的for range了。

字符串 & 数组

1
2
3
4
std::string str = “hello, world”;  
for(auto ch : str) {
std::cout << ch << std::endl;
}
1
2
3
4
int arr[] = {1, 2, 3, 4};  
for(auto i : arr) {
std::cout<< i << std::endl;
}

stl 容器

1
2
3
4
std::vector<std::string> str_vec = {“i”, “like”,  "google”};  
for(auto& it : str_vec) {
it = “c++”;
}

遍历stl map

1
2
3
4
std::map<int, std::string> hash_map = {{1, “c++”}, {2, “java”}, {3, “python”}};  
for(auto it : hash_map) {
std::cout << it.first << “\t” << it.second << std::endl;
}

算法

迭代器

对象

对象创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//隐式创建
Person p1;
cout<<endl;
Person p2(18, "lili");
cout<<endl;

//显式创建
Person p3 = Person();
cout<<endl;
Person p4 = Person(16, "xx");
cout<<endl;

// new 创建
Person *p5 = new Person();
cout<<endl;
Person *p6 = new Person(14, "yy");

VS 快捷键

1
2
多行注释:Ctrl+K+C(先按ctrl,再按K,最后按C)
取消多行注释:Ctrl+K+U(先按ctrl,再按K,最后按U)