string转char*

1
char *str = const_cast<char*>(str.c_str());

深拷贝

浅拷贝就是一个对象拷贝另一个对象的内容以后,其中一个对象的改变还会影响另一个对象的内容。

编译器默认拷贝构造函数是浅拷贝,如果在类中分配了堆内存,可能导致内存的重复释放。

为了解决浅拷贝的问题,通常需要自己实现拷贝构造函数,变为深拷贝。

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;
class Person{
public:
Person(int age, int height){
this->age = age;
this->height = new int(height);
}
Person(const Person& p){
this->age = p.age;
this->height = new int(*p.height);
}
~Person(){
cout<<"析构"<<endl;
delete height;
height = nullptr;
}
int age;
int* height;
};
int main(){
Person p1(18,190);
Person p2(p1);
cout<<p2.age<<" "<<*p2.height<<endl;
return 0;
}

linux常用命令

  • awk 参数 脚本 文件

    1
    awk '{print  NR NF "\t"  $0, $1 }'
  • wc -c 字符数 -l 行数 -w 单词数

  • sort

    1
    2
    sort -r coin.txt 逆序
    sort -n coin.txt 数值

partition函数

快排中的partition函数在algorithm库中有实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <algorithm>
using namespace std;
int main(){
//按照关键字划分 返回不满足条件的首个位置下标
vector<int> v{19,8,7,3,5,1,0};
int key = 6;
auto t = partition(v.begin(), v.end(), [key](int a){
return a < key;
}) - v.begin();
cout<<t<<endl;
for(auto x: v) cout<<x<<" ";
return 0;
}

bind函数

参数绑定。通用的函数适配器。接受一个可调用对象,生成一个新的可调用对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <algorithm>
#include <functional>
#include <queue>
using namespace std;
using std::placeholders::_1;
using std::placeholders::_2;
bool cmp( int a, int b){
return a > b;
}
int main(){
//使用bind()将函数转换为仿函数
//从大到小
vector<int> v{1,0,99,3,4,12,2};
sort(v.begin(), v.end(), bind(cmp, _1, _2));
for(auto x: v) cout<<x<<" ";
cout<<endl;
return 0;
}

lambda表达式

未命名的内联函数。一般来说应该减少捕获的数据量,避免捕获指针和引用。捕获列表和函数体是必不可少的。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <algorithm>
#include <functional>
#include <queue>
using namespace std;
int main(){
//lambda表达式 匿名函数对象
auto f = [](const int &a){
return a*a;
};
cout<<f(5)<<endl;
return 0;
}

function函数

函数指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <algorithm>
#include <functional>
#include <queue>
using namespace std;
int f(int a){
return a*a;
}
int main(){
//function是一个模板 相当于函数指针 function<return(args)> ff = func;
function<int(int)> ff = f;
cout<<ff(55)<<endl;
return 0;
}

tuple函数

优势在于传输一次性的一组数据,如果要大量使用,实际上不及结构体清晰明了。

可以将tuple用在map中作为多索引值。可以用来比较两个tuple类型的字典序大小。

tuple可以存引用

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
#include <iostream>
#include <algorithm>
#include <functional>
#include <queue>
#include <tuple>
#include <map>
#include <string>
using namespace std;

int main(){
//usage
std::tuple<int, string, char> t(32, "Penny", 'A');
//output
cout<<get<0>(t)<<endl; //32
get<1>(t) = "Alex"; //reference
//assignment
tuple<int, string, char> t2;
t2 = tuple<int, string, char>(24, "Alex", 'B');
t2 = make_tuple(17, "Jun", 'C');
//tuple can store reference
string st = "In for a penny";
tupel<string&> t3(st);
t3 = make_tuple(ref(st));
int x;
string y;
char z;
make_tuple(ref(x), ref(y), ref(z)) = t2;
tie(x, y, z) = t2; // doing the same thing
tie(x, std::ignore, z) = t2;
auto t4 = tuple_cat(t2, t3); //concatenate two string
//tuple traits
cout<<tuple_size<decltype(t4)>::value<<endl;
tuple_element<1, decltype(t4)>::type d;
cout<<d<<endl;
//Advantage
//comparison of tuples
tuple<int, int , int > time1, time2; //hours mintues seconds
if(time1 > time2) cout<<"time1 is a later time"<<endl;
//multi index map
map<tuple<int, char, float>, string> mp;
mp[make_tuple(18, 'A', 3.14)] = "Curiosity kills the cat";
//fast change value
int a, b, c;
tie(c, b, a) = make_tuple(a, b, c);
return 0;
}

一致性哈希

通过是为了解决传统哈希算法上的不足而提出来的。主要是增减机器是,传统哈希算法对于数据迁移的成本很高。一致性哈希也是使用取模的思想,只不过将哈希值空间组织成一个圆环,然后每个机器顺时针负责一段数据的存储,当增加机器或者减少机器时,把对应的数据迁移到附近的机器上就行,其他机器不用发生改动。但是存在一个不足,当机器数量比较少时,无法做到在环上均匀分布。于是引入虚拟节点,每个物理机器有相同数量的虚拟节点,通过虚拟节点去寻找位置,然后映射到物理机上。

海量数据问题

小根堆+归并

生成随机数

线性同余算法

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<iostream>
using namespace std;
//x_{n+1} = a*x_n + c mod m
//a = 48271
int main(){
long long a = 48271, c = 0, x = 1;
long long m = INT_MAX;
for(long long i = 0; i < 10; i++){
x = (a*x+c) % m;
cout<<x<<endl;
}
return 0;
}

什么是socket

socket是linux文件的一种类型。socket通过绑定IP和端口可以实现在进程间通信。创建一个socket文件描述符指向两个缓冲区,所以可以实现全双工通信。

epoll的优势

连接的文件描述符比较多,但是活跃的文件描述符比较少时,比select和poll效率更高。

epoll三个主要的函数:

  1. epoll_create()
  2. epoll_ctl()
  3. epoll_wait()

ET和LT的区别

  1. ET称为边沿触发,在调用epoll_wait函数监听的时候,如果有满足的事件,则该函数进行返回,返回值是满足监听事件的文件描述符数量。ET模式下,只会响应一次,即使部分文件描述符的事件没有进行处理。当发送接收缓冲区状态发生改变时,触发读写事件。边沿触发模式要求一直读写,直到读写完或者返回EAGAIN
  2. LT称为水平触发,如果epoll_wait有满足条件的事件,会一直返回。增加了epoll_wait函数的调用次数。所以通常在并发服务器上,多使用ET模式。如果系统中有大量不需要读写的就绪文件描述符,而它每次都会返回,这样会大大降低检索自己关心的就绪文件描述符的效率。接收缓冲区不为空,有数据可读,读事件一直触发。发送缓冲区不为满,有数据可写,则写事件一直触发。

ET为什么要用非阻塞而不是阻塞

  1. 阻塞模式,程序在读取数据时无法知道什么时候读完,会一直阻塞在read函数上,即使已经读完了数据。
  2. 非阻塞模式,程序使用while循环读取数据,读完以后read会返回,errno被设置为EAGAIN。

五种网络IO模型

  1. 阻塞IO
  2. 非阻塞IO
  3. IO多路复用
  4. 信号驱动IO
  5. 异步IO

前4个都是同步IO。IO多路复用也是阻塞的,但是没有阻塞在send和recv上,而是阻塞在select,poll和epoll上。

  1. 同步:进程在数据由内核空间复制回进程缓冲区时不能干别的事。
  2. 异步:在数据准备完成,由内核缓冲区拷贝到进程缓冲区后通知进程,在等待通知的这段事件里,进程可以干别的事情。