设计模式

单列模式

懒汉式

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 Singleton {
private:
Singleton() {

}
public:
static Singleton *GetSingleton() {
if (single == NULL) {
return single = new Singleton;
}
return single;
}
private:
static Singleton* single;
};
Singleton* Singleton::single = NULL;
int main(int argc, const char * argv[]) {
Singleton* s1 = Singleton::GetSingleton();
Singleton* s2 = Singleton::GetSingleton();
if (s1 == s2) {
cout << "s1 == s2" << endl;
} else {
cout << "s1 != s2" << 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
class Singleton {
private:
Singleton() {

}
public:
static Singleton *GetSingleton() {
// if (single == NULL) {
// return single = new Singleton;
// }
return single;
}
private:
static Singleton* single;
};
Singleton* Singleton::single = new Singleton;
int main(int argc, const char * argv[]) {

Singleton* s1 = Singleton::GetSingleton();
Singleton* s2 = Singleton::GetSingleton();
if (s1 == s2) {
cout << "s1 == s2" << endl;
} else {
cout << "s1 != s2" << 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

class Fruit {
public:
virtual void sayName() {
cout << "Fruit" << endl;
}
};

class SouthBanana : public Fruit {
public:
virtual void sayName() {
cout << "SouthBanana" << endl;
}
};

class SouthApple : public Fruit {
public:
virtual void sayName() {
cout << "SouthApple" << endl;
}
};

class NorthBanana : public Fruit {
public:
virtual void sayName() {
cout << "NorthBanana" << endl;
}
};

class NorthApple : public Fruit {
public:
virtual void sayName() {
cout << "NorthApple" << endl;
}
};

class FruitFactory {
public:
virtual Fruit* getBanana() {
cout << "FruitFactory:getBanana " << endl;
return NULL;
}
virtual Fruit* getApple() {
cout << "FruitFactory:getApple " << endl;
return NULL;
}
};

class SouthFactory : public FruitFactory {
public:
virtual Fruit* getBanana() {
return new SouthBanana;
}
virtual Fruit* getApple() {
return new SouthApple;
}
};

class NorthFactory : public FruitFactory {
public:
virtual Fruit* getBanana() {
return new NorthBanana;
}
virtual Fruit* getApple() {
return new NorthApple;
}
};

int main(int argc, const char * argv[]) {
FruitFactory *ff = NULL;
Fruit* fruit = NULL;

ff = new SouthFactory;
fruit = ff->getBanana();
fruit->sayName();

delete fruit;
delete ff;

return 0;
}

数据结构与算法

stach

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
class Teacher {
private:
int age;
char* name;
public:

Teacher() {
age = 18;
name = new char[10];
strcpy(name, "default");
}
Teacher(int _age, char* _name) {
age = _age;
name = new char[strlen(_name) + 1];
strcpy(name, _name);
}
Teacher(Teacher& obj) {
age = obj.age;
name = new char[strlen(obj.name) + 1];
strcpy(name, obj.name);
}
~Teacher() {
age = 0;
if (name != NULL) {
delete [] name;
name = NULL;
}
}
Teacher& operator=(Teacher& obj) {
age = obj.age;
if (name != NULL) {
delete [] name;
name = NULL;
}

name = new char[strlen(obj.name) + 1];
strcpy(name, obj.name);

return *this;
}

char* getName() {
return name;
}
int getAge() {
return age;
}
};

template <typename T>
struct MyLink {
T* data;
MyLink<T>* prev;
};

template <typename T>
class MyStack {
private:
int _size = 0;
MyLink<T>* head;
public:
MyStack() {
head = new MyLink<T>;
head->data = NULL;
head->prev = NULL;
}
~MyStack() {
clear();
if (head != NULL) {
if (head->data != NULL) {
delete head->data;
head->data = NULL;
}

if (head->prev != NULL) {
delete head->prev;
head->prev = NULL;
}
}
}

int push(T* objP) {
MyLink<T>* p = new MyLink<T>;
p->data = objP;
p->prev = head;
head = p;
_size++;

return _size;
}

T* pop() {
if (_size == 0) {
return NULL;
}

MyLink<T>* curr = head;
head = head->prev;
_size--;
return curr->data;
}

int size() {
return _size;
}

void clear() {
if (_size == 0) {
return;
}

while (_size > 0) {
pop();
}
}
};
int main(int argc, const char * argv[]) {
Teacher t1(1, "t1"), t2(2, "t2"), t3(3, "t3");
MyStack<Teacher> s = MyStack<Teacher>();
s.push(&t1);
s.push(&t2);
s.push(&t3);

while (s.size()) {
Teacher* tmp = s.pop();
cout << "name: " << tmp->getName() << " age: " << tmp->getAge() << 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
#include <stdio.h>

typedef struct {
int data;
struct BiTNode* lChild, *rChild;
}BiTNode, *BiTree;

void printData(int num) {
printf(" %d ", num);
}

void inOrder(BiTNode* T) {
if (T == NULL) {
return;
}
inOrder(T->lChild);
printData(T->data);
inOrder(T->rChild);
}

BiTNode* copyTree(BiTNode* T) {
if (T == NULL) {
return NULL;
}
BiTNode* newNode = NULL;
BiTNode* newLChild = NULL;
BiTNode* newRChild = NULL;

if (T->lChild != NULL) {
newLChild = copyTree(T->lChild);
}

if (T->rChild != NULL) {
newRChild = copyTree(T->rChild);
}

newNode = (BiTNode*)malloc(sizeof(BiTNode));
memset(newNode, 0, sizeof(BiTNode));
if (newNode == NULL) {
return NULL;
}
newNode->data = T->data;
newNode->lChild = newLChild;
newNode->rChild = newRChild;


return newNode;
}

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

BiTNode t1, t2, t3, t4, t5;
memset(&t1, 0, sizeof(BiTNode));
memset(&t2, 0, sizeof(BiTNode));
memset(&t3, 0, sizeof(BiTNode));
memset(&t4, 0, sizeof(BiTNode));
memset(&t5, 0, sizeof(BiTNode));
t1.data = 1;
t1.lChild = &t2;
t1.rChild = &t3;
t2.data = 2;
t2.lChild = &t4;
t3.data = 3;
t3.rChild = &t5;
t4.data = 4;
t5.data = 5;

printf("\nold tree: \n");
inOrder(&t1);

printf("\nnew tree: \n");
BiTNode* newT = NULL;
newT = copyTree(&t1);
inOrder(newT);

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
#include <stack>
struct BiTNode{
int data;
struct BiTNode* lChild, *rChild;
};

void printData(int num) {
printf(" %d ", num);
}

void inOrder(BiTNode* T) {
if (T == NULL) {
return;
}
inOrder(T->lChild);
printData(T->data);
inOrder(T->rChild);
}


BiTNode* goLeft(BiTNode* T,stack<BiTNode*> &s) {
if (T == NULL) {
return NULL;
}

//判断是否有左节点, 如果有就入栈, 否则就返回当前节点
while (T->lChild != NULL) {
s.push(T);
T = T->lChild;
}
return T;
}

void inOrder2(BiTNode* T) {
if (T == NULL) {
return;
}

stack<BiTNode*> s;

//步骤1
T = goLeft(T, s);

while (T) {
printData(T->data);
if (T->rChild) {
//如果有右子树 就重复步骤1
T = goLeft(T->rChild, s);
} else if (!s.empty()) {
//如果没右子树并且栈顶不为空, 则取出栈顶, 回退到栈顶元素
T = s.top();
s.pop();
} else {
T = NULL;
}
}
}


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

BiTNode t1, t2, t3, t4, t5;
memset(&t1, 0, sizeof(BiTNode));
memset(&t2, 0, sizeof(BiTNode));
memset(&t3, 0, sizeof(BiTNode));
memset(&t4, 0, sizeof(BiTNode));
memset(&t5, 0, sizeof(BiTNode));
t1.data = 1;
t1.lChild = &t2;
t1.rChild = &t3;
t2.data = 2;
t2.lChild = &t4;
t3.data = 3;
t3.rChild = &t5;
t4.data = 4;
t5.data = 5;

printf("\n递归方法: \n");
inOrder(&t1);

printf("\n非递归方法: \n");
inOrder2(&t1);

return 0;
}

Morris方法遍历二叉树

  1. 如果当前节点的左孩子为空,则输出当前节点并将其右孩子作为当前节点。

  2. 如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点。

    a) 如果前驱节点的右孩子为空,将它的右孩子设置为当前节点。当前节点更新为当前节点的左孩子。

    b) 如果前驱节点的右孩子为当前节点,将它的右孩子重新设为空(恢复树的形状)。输出当前节点。当前节点更新为当前节点的右孩子。

  3. 重复以上1、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
void inorderMorrisTraversal(BiTNode *root) {
BiTNode* curr = NULL, *prev = NULL;

while (curr != NULL) {
if (curr->lChild == NULL) {
//1. 如果当前节点的左孩子为空,则输出当前节点并将其右孩子作为当前节点。
printData(curr->data);
curr = curr->rChild;
} else {
prev = curr->lChild;
while (prev->rChild != NULL && prev->rChild != curr) {
prev = curr->rChild;
}

if (prev->rChild == NULL) {
//a) 如果前驱节点的右孩子为空,将它的右孩子设置为当前节点。当前节点更新为当前节点的左孩子。
prev->rChild = curr;
curr = curr->lChild;
} else {
//b) 如果前驱节点的右孩子为当前节点,将它的右孩子重新设为空(恢复树的形状)。输出当前节点。当前节点更新为当前节点的右孩子。
printData(curr->data);
curr = curr->rChild;
prev->rChild = NULL;
}
}
}

}

参考

插入排序

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
void printArr(int* arr, int len) {
printf("开始打印: ");
for (int i = 0; i < len; i ++) {
printf(" %d ", arr[i]);
}
}
void selectSort(int *arr, int len) {

for (int i = 1; i < len; i++) {
int tmp = arr[i];
int k = i;

for (int j = i - 1; (j >= 0) && (arr[j] > tmp); j--) {
arr[j + 1] = arr[j];
k = j;
}

arr[k] = tmp;
}
}

int main(int argc, const char * argv[]) {
int arr[] = {2,1,32,12,42,5};
int len = sizeof(arr) / sizeof(int);
selectSort(arr, len);
printArr(arr, len);

return 0;
}

希尔排序

与插入排序类似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void shellSort(int *arr, int len) {
int i = 0, j = 0, k = -1, temp = -1, gap = len;
do {
gap = gap / 3 + 1; //业界统一实验的结果

for (i = gap; i < len; i += gap) {
k = i;
temp = arr[k];

for (j = i - gap; (j >= 0) && (arr[j] > temp); j -= gap) {
arr[j + gap] = arr[j];
k = j;
}
arr[k] = temp;
}
} while (gap > 1);
}

冒泡排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void bubbleSort(int *arr, int len) {
int flag = 1; //0表示没有发生交换, 1表示发生交换

for (int i = 0; (i < len) && flag; i++) {
flag = 0;
for (int j = len - 1; j > i; j--) {
if (arr[j - 1] > arr[j]) {
int tmp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = tmp;
flag = 1;//发生了交换
}
}
}
}

快速排序

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
void swap(int *arr, int low, int high) {
int temp = arr[low];
arr[low] = arr[high];
arr[high] = temp;
}

int partition(int *arr, int low, int high) {
//找出基准值
int pv = arr[low];
while (low < high) {
//找出比基准值小的数 并交换
while ((low < high) && arr[high] >= pv) {
high--;
}
swap(arr, low, high);
//找出比基准值大的数 并交换
while ((low < high) && arr[low] <= pv) {
low++;
}
swap(arr, low, high);
}

return low;
}

void quickSort(int *arr, int low, int high) {
if (low < high) {
//分区
int mid = partition(arr, low, high);
//分治左边
quickSort(arr, low, mid - 1);
//分治右边
quickSort(arr, mid + 1, high);
}
}

Drawing

createLinearGradient(oriX, oriY, desX, desY)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d');
function createLinearGradient() {
var linearGradient = context.createLinearGradient(0, 0, canvas.width, canvas.height);

linearGradient.addColorStop(0, 'blue');
linearGradient.addColorStop(0.25, 'yellow');
linearGradient.addColorStop(0.5, 'green');
linearGradient.addColorStop(0.75, 'white');
linearGradient.addColorStop(1, 'black');
context.fillStyle = linearGradient;
context.fillRect(0, 0, canvas.width, canvas.height);
}
createLinearGradient()

createRadialGradient(oriX, oriY, oriR, desX, desY, desR)

1
2
3
4
5
6
7
8
9
10
11
12
function createRadialGradient() {
var linearGradient = context.createRadialGradient(canvas.width / 2, canvas.height, 10, canvas.width / 2, 0, 100);

linearGradient.addColorStop(0, 'blue');
linearGradient.addColorStop(0.25, 'yellow');
linearGradient.addColorStop(0.5, 'green');
linearGradient.addColorStop(0.75, 'white');
linearGradient.addColorStop(1, 'black');
context.fillStyle = linearGradient;
context.fillRect(0, 0, canvas.width, canvas.height);
}
createRadialGradient()

createPattern(HTMLImageElement | HTMLCanvasElement | HTMLVideoElement image, ‘repeat|repeat-x|repeat-y|no-repeat’)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function createCanvasPattern() {
var SHADOW_COLOR = 'rgba(0,0,0,0.7)';
var image = new Image();
image.src = "https://img.alicdn.com/tfs/TB1sLMKe8fH8KJjy1XbXXbLdXXa-300-300.jpg";
image.onload = function() {
var pattern = context.createPattern(image, "no-repeat");
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = pattern;
context.shadowColor = SHADOW_COLOR;
context.shadowOffsetX = 10;
context.shadowOffsetY = 10;
context.shadowBlur = 10;
context.fillRect(0, 0, canvas.width, canvas.height);
}
}
createCanvasPattern();

Paths, Stroking, and Filling

  • arc(x, y, r, oriPI, desPI, true/false(是否顺时针))
  • beginPath() 开始新的路径绘图
  • closePath() 闭合路径
  • fill() 根据fillStyle填充图形
  • rect(double x, double y, double width, double height) 绘制矩形路径
  • stroke() 根据strokeStyle绘制路径

划线

如果1px宽度的线刚好落在像素的边界上, 会导致线的宽度变成2px

1
2
3
4
5
6
7
8
9
context.lineWidth = 1;
context.beginPath();
context.moveTo(50, 10);
context.lineTo(450, 10);
context.stroke();
context.beginPath();
context.moveTo(50.5, 50.5);
context.lineTo(350.5, 50.5);
context.stroke();

划虚线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function drawDashedLine(context, x1, y1, x2, y2, dashLen = 5) {
var dx = x2 - x1;
var dy = y2 - y1;
var dashNum = 0;

if (dx === 0) {
dashNum = Math.abs(dy / dashLen);
} else if (dy === 0) {
dashNum = Math.abs(dx / dashLen);
} else {
dashNum = Math.floor(Math.sqrt(dx * dx, dy * dy) / dashLen);
}

context.beginPath();
for (var i = 0; i < dashNum; i++) {
context[i % 2 === 0 ? "lineTo" : "moveTo"](x1 + dx / dashNum * i, y1 + dy / dashNum * i);
}
context.stroke();
}

drawDashedLine(context, 20, 20, 220, 20);
drawDashedLine(context, 220, 20, 220, 280);
drawDashedLine(context, 220, 280, 20, 280);
drawDashedLine(context, 20, 280, 20, 20);

CanvasRenderingContext2D

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
var moveToFunction = CanvasRenderingContext2D.prototype.moveTo;
CanvasRenderingContext2D.prototype.lastMoveToLocation = {};
CanvasRenderingContext2D.prototype.moveTo = function (x, y) {
moveToFunction.apply(context, [x,y]);
this.lastMoveToLocation.x = x;
this.lastMoveToLocation.y = y;
};
CanvasRenderingContext2D.prototype.dashedLineTo = function (x, y, dashLength) {
dashLength = dashLength === undefined ? 5 : dashLength;
var startX = this.lastMoveToLocation.x;
var startY = this.lastMoveToLocation.y;
var deltaX = x - startX;
var deltaY = y - startY;
var numDashes = Math.floor(Math.sqrt(deltaX * deltaX
+ deltaY * deltaY) / dashLength);
for (var i=0; i < numDashes; ++i) {
this[ i % 2 === 0 ? 'moveTo' : 'lineTo' ]
(startX + (deltaX / numDashes) * i,
startY + (deltaY / numDashes) * i);
}
this.moveTo(x, y);
};

context.lineWidth = 3;
context.strokeStyle = 'blue';
context.moveTo(20, 20);
context.dashedLineTo(context.canvas.width-20, 20);
context.dashedLineTo(context.canvas.width-20,
context.canvas.height-20);
context.dashedLineTo(20, context.canvas.height-20);
context.dashedLineTo(20, 20);
context.dashedLineTo(context.canvas.width-20,
context.canvas.height-20);

context.stroke();

Line joins

  • miter(default)
  • bevel
  • round

arcTo(x1, y1, x2, y2, radius)

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
(function () {
function roundedRect(cornerX, cornerY,
width, height, cornerRadius) {
if (width > 0)
context.moveTo(cornerX + cornerRadius, cornerY);
else
context.moveTo(cornerX - cornerRadius, cornerY);

context.arcTo(cornerX + width, cornerY,
cornerX + width, cornerY + height,
cornerRadius);
context.arcTo(cornerX + width, cornerY + height,
cornerX, cornerY + height,
cornerRadius);
context.arcTo(cornerX, cornerY + height,
cornerX, cornerY,
cornerRadius);
if (width > 0) {
context.arcTo(cornerX, cornerY,
cornerX + cornerRadius, cornerY,
cornerRadius);
}
else {
context.arcTo(cornerX, cornerY,
cornerX - cornerRadius, cornerY,
cornerRadius);
}
}

function drawRoundedRect(strokeStyle, fillStyle, cornerX, cornerY,
width, height, cornerRadius) {
context.beginPath();
roundedRect(cornerX, cornerY, width, height, cornerRadius);
context.strokeStyle = strokeStyle;
context.fillStyle = fillStyle;
context.stroke();
context.fill();
}

drawRoundedRect('blue', 'yellow', 50, 40, 100, 100, 10);
drawRoundedRect('purple', 'green', 275, 40, -100, 100, 20);
drawRoundedRect('red', 'white', 300, 140, 100, -100, 30);
drawRoundedRect('white', 'blue', 525, 140, -100, -100, 40);

})()

Vim实用技巧

Vim 解决问题的方式

按键操作 用途
f{char} 查找字符
; 命令会重复查找上次 f 命令所查找的字符(f查找多次可以使用;一次实现)
:s/target/replacement 替换内存
* 光标会跳到下一个匹配项上, 所有出现这个 词的地方都会被高亮显示出来(:set hls)

普通模式

按键操作 用途
db 向前删除
daw 删除整个字符
dap 删除整个段落
{num}<C-a> 对数字进行++
{num}<C-x> 对数字进行–
d{num}w 删除单词
> 增加缩进
< 减少缩进
= 自动缩进
g~ 反转大小写
gu 转换为小写
gU 转换为大写

插入模式

按键操作 用途
<C-h> 删除前一个字符(同退格键)
<C-w> 删除前一个单词
<C-u\> 删至行首
<C-r>={运算} 获取计算结果
<C-v>{065} 插入ASCII码
<C-v>u{1234} 插入utf8字符
ga 查看字符编码信息
<C-k>{char1}{char2} 插入以二合字母{char1}{char2}表示的字符

可视模式

按键操作 用途
<C-v> 激活面向列块的可视模式
gv 重选上次的高亮选区
o 在选择模式下可以切换活动端点
vit 选择标签里的内容
按键操作 用途
‘m 包含位置标记 m 的行
‘< 高亮选区的起始行
‘> 高亮选区的结束行
% 整个文件

复制移动和复制行

按键操作 用途
:.6t. 把第6行复制到当前行下方
:t6 把当前行复制第6行
:t. 复制当前行
:t$ 把当前行复制到文本结尾
:'<,'>t0 把高亮选中的行复制到文件开头
:[range]move {address} 移动行

The canvas Element

css设置canvas大小和canvas元素节点上设置大小区别

css只会设置元素大小, 无法改变画布表面大小
元素节点设置不仅改变元素大小, 也改变画布表面大小

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
body {
background: aqua;
}
#canvas {
background: #fff;
/* width: 600px;
height: 600px; */
margin-left: 200px;
}
</style>
</head>
<body>
<canvas id="canvas" width="600" height="600"></canvas>
<img id="img"/>
</body>
<script>
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
FONT_HEIGHT = 15,
MARGIN = 35,
HAND_TRUNCATION = canvas.width/25,
HOUR_HAND_TRUNCATION = canvas.width/10,
NUMERAL_SPACING = 20,
RADIUS = canvas.width/2 - MARGIN,
HAND_RADIUS = RADIUS + NUMERAL_SPACING;

function drawCicle() {
context.beginPath();
context.arc(canvas.width / 2, canvas.height / 2, RADIUS, 0, 2*Math.PI, true);
context.stroke();
}
function drawCenter() {
context.beginPath();
context.arc(canvas.width / 2, canvas.height / 2, 5, 0, 2 * Math.PI, true);
context.fill();
}
function drawHand(loc, isHour, lineWidth) {
var angle = 2 * Math.PI * (loc / 60) - Math.PI / 2;
handRadius = isHour ? RADIUS - HAND_TRUNCATION-HOUR_HAND_TRUNCATION : RADIUS - HAND_TRUNCATION;

context.moveTo(canvas.width / 2, canvas.height / 2);
context.lineTo(canvas.width / 2 + handRadius * Math.cos(angle), canvas.height / 2 + handRadius * Math.sin(angle));
context.lineWidth = lineWidth;
context.stroke();
}
function drawHands() {
var date = new Date();
var hour = date.getHours();

hour = hour > 12 ? hour - 12 : hour;

drawHand(hour * 5 + date.getMinutes() / 60 * 5, true, 5);
drawHand(date.getMinutes(), false, 2);
drawHand(date.getSeconds(), false, 0.3);
}
function drawNumbers() {
var numberList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var angle = 0;
var numeralWidth = 0;

numberList.forEach(function(number) {
angle = 2 * Math.PI * (number - 3) / 12;
numeralWidth = context.measureText(number).width;
context.fillText(number, canvas.width / 2 + HAND_RADIUS * Math.cos(angle) - numeralWidth / 2, canvas.height / 2 + HAND_RADIUS * Math.sin(angle) - FONT_HEIGHT / 3);
});
}
function drawClock() {
context.clearRect(0, 0, canvas.width, canvas.height);
drawCicle();
drawCenter();
drawHands();
drawNumbers();
}
context.font = FONT_HEIGHT + 'px Arial';
// drawClock();
// setInterval(drawClock, 1000);
var image = new Image();
image.src = "https://mdn.mozillademos.org/files/225/Canvas_drawimage.jpg";
canvas.onmousedown = function (event) {
var oriX = event.clientX, oriY = event.clientY;
var bbox = canvas.getBoundingClientRect();
window.onmousemove = function (event) {
var x = event.clientX, y = event.clientY;
context.clearRect(0, 0, canvas.width, canvas.height);
context.strokeRect(oriX - bbox.left, oriY - bbox.top, x - oriX, y - oriY);
window.onmouseup = function (event) {
var x = event.clientX, y = event.clientY;
window.onmousemove = null;
window.onmousedown = null;
context.drawImage(image, oriX - bbox.left, oriY - bbox.top, x - oriX, y - oriY)
setTimeout(function() {
var src = canvas.toDataURL("image/png");
console.log(src);
var img = document.getElementById("img");
img.setAttribute('crossOrigin', 'anonymous');
img.src = src;
}, 1000)
}
}
}
</script>
</html>

使用向量计算碰撞

单元向量 unit vector

1
2
3
4
5
var vectorMagnitude = Math.sqrt(Math.pow(vector.x, 2) +
Math.pow(vector.y, 2)),
unitVector = new Vector();
unitVector.x = vector.x / vectorMagnitude;
unitVector.y = vector.y / vectorMagnitude;

向量相加 相减

1
2
3
4
5
6
7
var vectorSum = new Vector();
vectorSum.x = vectorOne.x + vectorTwo.x;
vectorSum.y = vectorOne.y + vectorTwo.y;

var vectorSubtraction = new Vector();
vectorSubtraction.x = vectorOne.x - vectorTwo.x;
vectorSubtraction.y = vectorOne.y - vectorTwo.y;

The Dot Product of Two Vectors 向量点积 计算2个物品是否会碰撞

1
var dotProduct = vectorOne.x * vectorTwo.x + vectorOne.y * vectorTwo.y; //正数同方向, 负数反方向

计算速度

1
2
3
pixels / frame = (X ms / frame) * (Y pixels / second);
// ===> 推到出
pixels / frame = X*Y / 1000;

linux进程

进程

fork

当程序遇到fork时, 就会fork出一个子进程, 子进程返回0, 父进程返回子进程的pid, 子进程会拷贝父进程的数据空间、堆、栈等资源成副本, 这意味着父子进程间不共享这些存储空间, 因为拥有一样的堆栈数据结构, 子进程会按照当前fork的地方开始执行, 而不是重头开始执行

孤儿进程和僵尸进程

如果父进程先退出, 子进程还没退出那么会将父进程会变成init进程(即1号进程), 称为孤儿进程; 如果子进程先退出, 父进程还没有退出, 那么子进程必须等到父进程捕捉到子进程的退出状态进行清理回收子进程才真正结束, 否则这个时候子进程就成为僵尸进程
通过signal(SIGCHLD, SIG_IGN)通知内核对子进程的结束不关心,由内核回收。如果不想让父进程挂起,可以在父进程中加入一条语句:signal(SIGCHLD,SIG_IGN);表示父进程忽略SIGCHLD信号,该信号是子进程退出的时候向父进程发送的。

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 <sys/types.h>
#include <unistd.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <signal.h>
#include <errno.h>
#include <signal.h>

#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, const char * argv[]) {
pid_t pid;
signal(SIGCHLD, SIG_IGN);// 忽略SIGCHLD信号
printf("before fork pid: %d \n", getpid());

int abc = 10;
pid = fork();

if (pid == -1) {
perror("error");
return -1;
}

printf("~~pid: %d \n", pid);

if (pid > 0) {
abc ++;
printf("parent: pid: %d \n", getpid());
printf("abc: %d \n", abc);
}
else if (pid == 0) {
abc ++;
printf("child: %d, parent: %d \n", getpid(), getppid());
printf("abc: %d\n", abc);
sleep(110);
}

printf("after fork \n");
return 0;
}
1
2
3
4
5
6
// ps -ef|grep TestProcess
501 11001 11002 0 7:27上午 ?? 0:00.11 /Users/lichen/Library/Developer/Xcode/DerivedData/TestProcess-dainbcprniakdaelfdtrmjuxyyio/Build/Products/Debug/TestProcess
501 11004 11001 0 7:27上午 ?? 0:00.00 /Users/lichen/Library/Developer/Xcode/DerivedData/TestProcess-dainbcprniakdaelfdtrmjuxyyio/Build/Products/Debug/TestProcess

//父进程退出后
501 11004 1 0 7:27上午 ?? 0:00.00 /Users/lichen/Library/Developer/Xcode/DerivedData/TestProcess-dainbcprniakdaelfdtrmjuxyyio/Build/Products/Debug/TestProcess

vfork

vfork与fork的最大区别是 vfork会共享父进程的数据空间, 子进程先运行, 父进程后运行;

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
#include <sys/types.h>
#include <unistd.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <signal.h>
#include <errno.h>
#include <signal.h>

#include <sys/stat.h>
#include <fcntl.h>

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

pid_t pid;
signal(SIGCHLD, SIG_IGN);
printf("before fork pid: %d \n", getpid());

int abc = 10;
pid = vfork();

if (pid == -1) {
perror("error");
return -1;
}

printf("~~pid: %d \n", pid);

if (pid > 0) {
abc ++;
printf("parent: pid: %d \n", getpid());
printf("abc: %d \n", abc);
}
else if (pid == 0) {
abc ++;
printf("child: %d, parent: %d \n", getpid(), getppid());
printf("abc: %d\n", abc);
char *const argv[] = {"ls", "-al", NULL};
int ret = execve("/bin/ls", argv, NULL);
if (ret == -1) {
perror("execve: ");
}
exit(0);
}

printf("after fork \n");
return 0;
}

exit 与 _exit的区别

exit是c语言方法, 调用后, 会调用终止处理程序和清空I/O缓存区并输出, 再调用内核终止进程; _exit是系统方法, 会跳过中间步骤, 直接调用内核终止程序, 因此在I/O缓存区的内容无法打印到屏幕上

守护进程

int daemon(int nochdir, int noclose);

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
#include <sys/types.h>
#include <unistd.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <signal.h>
#include <errno.h>
#include <signal.h>

#include <sys/stat.h>
#include <fcntl.h>

// 一下逻辑与 int daemon(int nochdir, int noclose); 函数相似
int main(int argc, const char * argv[]) {
int i = 0;
pid_t pid;
signal(SIGCHLD, SIG_IGN);
printf("before fork pid: %d \n", getpid());

pid = fork();

if (pid == -1) {
perror("fork");
return -1;
}

pid = setsid();//新建会话id

if (pid == -1) {
perror("setsid");
return -1;
} else if (pid > 0) {
exit(0);
}

chdir("/");

for (i = 0; i < 3; i++) {
close(i);
}

open("/dev/null", O_RDWR);
dup(0);
dup(1);

while (1) {
sleep(1);
}

return 0;
}

wait-system

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
#include <sys/types.h>
#include <unistd.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <signal.h>
#include <errno.h>
#include <signal.h>

#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>


int main(int argc, const char * argv[]) {
int i = 0, j = 0, procNum = 0, count = 0, ret = 0;
pid_t pid;
signal(SIGCHLD, SIG_IGN);
printf("before fork pid: %d \n", getpid());

printf("plz input procNum: ");
scanf("%d", &procNum);

printf("plz input count: ");
scanf("%d", &count);

for (i = 0; i < procNum; i++) {
pid = fork();

if (pid == -1) {
perror("fork");
return -1;
}

if (pid == 0) {
for (j = 0; j < count; j++) {
printf("child process dosomething\n");
sleep(1);
}
exit(0);
}
}

while (1) {
ret = wait(NULL);
if (ret == -1) {
if (errno == EINTR) { // 判断是否被别的信号异常中断, 做异常处理
continue;
}
break;
}
}

printf("parent process out\n");


return 0;
}

自定义signal信号行为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void handle(int num) {
printf("handle ..., %d, %d", num, SIGQUIT);
if (num == SIGQUIT) {
// exit(0);
}
}

int main(int argc, const char * argv[]) {
char tmpchar;
//重写默认信号行为, 当有信息时, 系统会调用handle方法
signal(SIGINT, handle);

while ( (tmpchar = getchar()) != 'a' ) {
pause();
}

//恢复默认信号行为
signal(SIGINT, SIG_DFL);

while (1) {
pause();
}
return 0;
}

深入理解计算机系统

为什么32位系统只能支持4G内存?

因为每台计算机都有一个字长, 这也是指针的大小, 32位系统的指针字长为4字节, 也就是32位, 因此它能表示的地址为 0 ~ 2^32 - 1, 所以超出4G的内存地址, 它是无法表示的

int, long, int32_t, int64_t类型的区别?

long 类型在32位系统是4字节, 在64位是8字节, 为了不同的编译系统导致不同的分配字节, 因此引入了int32_t, int64_t类型

逻辑位移和算数位移

逻辑位移向右位移时, 高位以0补充, 算数位移向右位移高位以最高有效值补充, 一般无符号以逻辑位移方式, 有符号位移按算数位移方式

无符号与有符号进行运算

无符号与有符号数进行运算, 有符号数被会转成无符号数进行运算

1
2
3
4
5
6
7
8
int main(int argc, const char * argv[]) {
unsigned int a = 1;
int b = -2;
cout << "result: " << a + b << " 2^32: " << pow(2, 32) << " a+b-2^32: " << a + b - pow(2, 32) << endl;
//result: 4294967295 2^32: 4.29497e+09 a+b-2^32: -1

return 0;
}

unsigned 导致的BUG

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
float sum_elements(float a[], unsigned length)  
{
int i = 0;
float sum = 0;
//length为无符号 length-1的结果也是无符号, 所以不管结果是正数还是负数, 对于程序猿而言都是大于0的
for(i = 0; i <= length -1; ++i)
sum += a[i];
return sum;
}

//size_t ==> unsigned int
size_t strlen(const char *s);

int strlonger(char *s1, char *s2)
{
//因为strlen返回的结果是unsigned int 所以下面的结果都是大于0的 应改为strlen(s1) > strlen(s2)
return strlen(s1) - strlen(s2) > 0;
}

汇编

寄存器

63 31 15 7 0
%rax %eax %ax %al 返回值
%rbx %ebx %bx %bl 被调用者保存
%rcx %ecx %cx %cl 第4个参数
%rdx %edx %dx %dl 第3个参数
%rsi %esi %si %sil 第2个参数
%rdi %edi %di %dil 第1个参数
%rbp %ebp %bp %bpl 被调用者保存
%rsp %esp %sp %spl 栈指针
%r8 %r8d %r8w %r8b 第5个参数
%r9 %r9d %r9w %r9b 第6个参数
%r10 %r10d %r10w %r10b 调用者保存
%r11 %r11d %r11w %r11b 调用者保存
%r12 %r12d %r12w %r12b 被调用者保存
%r13 %r13d %r13w %r13b 被调用者保存
%r14 %r14d %r14w %r14b 被调用者保存
%r15 %r15d %r15w %r15b 被调用者保存
  • movb(传送字节)
  • movw(传送字)
  • movl(传送双字 long word)
  • movq(传送四字)
  • movs(传送单精读)
  • movl(传送双精度) 与传送双字一样 但是浮点运送用的是完全不同的指令和寄存器 因此不会有冲突

操作数指示符

类型 格式 操作数
立即数 $Imm Imm
寄存器 ra R[ra]
内存 Imm M[Imm]
内存 (ra) M[R[ra]]
内存 Imm(ra) M[Imm + R[ra]]
内存 (rb, ri) M[R[rb] + R[ri]]
内存 Imm(rb, ri) M[Imm + R[rb] + R[ri]]
内存 (, ri, s) M[R[ri] * s]
内存 Imm(, ri, s) M[Imm + R[ri] * s]
内存 (rb, ri, s) M[R[rb] + R[ri] * s]
内存 Imm(rb, ri, s) M[Imm + R[rb] + R[ri] * s]

MOV S, D

  • MOVZ 目的中剩余的z字节填充0
  • MOVS 目的中剩余的z字节填充符号位

间接引用指针实际上就是从寄存器中取出指针所保存的地址, 然后从内存找到相应的地址(%rdi); 局部变量通常是放在寄存器中;

long exchange(long* xp, long y)  {
        long x = *xp;
        *xp = y;
        return x;
}

//对应的汇编代码
// xp => %rdi  y ==> %rsi
movq    (%rdi), %rax
movq    %rsi, (%rdi)
ret

条件码

  • CF: 进位标志
  • ZF: 零标志
  • SF: 符号标志
  • OF: 溢出标志

JavaScript设计模式与开发实践

高阶函数实现AOP

AOP(面向切面编程)的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,这些 跟业务逻辑无关的功能通常包括日志统计、安全控制、异常处理等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Function.prototype.before = function( beforefn ) {
var __self = this; // 保存原函数的引用
return function(){ // 返回包含了原函数和新函数的"代理"函数
beforefn.apply( this, arguments ); // 执行新函数,修正 this
return __self.apply( this, arguments );// 执行原函数
}
}
Function.prototype.after = function( afterfn ){
var __self = this;
return function(){
var ret = __self.apply( this, arguments ); //执行前面的函数
afterfn.apply( this, arguments ); //调用最后的函数
return ret;
}
};
var func = function(){
console.log( 2 );
};
func = func.before(function(){
console.log( 1 );
}).after(function(){
console.log( 3 );
});
func();

uncurrying

1
2
3
4
5
6
7
8
9
10
11
12
Function.prototype.uncurrying = function(){
var self = this;
return function(){
var obj = Array.prototype.shift.call( arguments );
return self.apply( obj, arguments );
}
};
var push = Array.prototype.push.uncurrying();
(function(){
push( arguments, 4 );
console.log( arguments ); // 输出:[1, 2, 3, 4]
})( 1, 2, 3 );
1
2
3
4
5
6
7
//第二种实现
Function.prototype.uncurrying = function(){
var self = this;
return function(){
return Function.prototype.call.apply( self, arguments );
}
};

函数节流

一次性渲染太多影响性能, 所以分批进行渲染

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
var timeChunk = function( ary, fn, count ){
var obj, t;
var len = ary.length;
var start = function(){
for( var i = 0; i < Math.min( count || 1, ary.length ); i++ ){
var obj = ary.shift();
fn( obj );
}
};

return function(){
t = setInterval(function(){
if ( ary.length === 0 ){ // 如果全部节点都已经被创建好
return clearInterval( t ); }
start();
}, 200 ); // 分批执行的时间间隔,也可以用参数的形式传入
};
};

var ary = [];
for ( var i = 1; i <= 1000; i++ ){
ary.push( i );
};
var renderFriendList = timeChunk( ary, function( n ){
var div = document.createElement( 'div' );
div.innerHTML = n;
document.body.appendChild( div );
}, 8 );
renderFriendList();

单列模式

1
2
3
4
5
6
7
8
var getSingle = function( fn ){
var result;
return function(){
return result || ( result = fn .apply(this, arguments ) );
}
};
var createLoginLayer = function() {}//创建登陆框的逻辑
var createSingleLoginLayer = getSingle( createLoginLayer );

用单例模式代替once

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
//让div的click事件只触发一次
var bindEvent = function(){
$( 'div' ).one( 'click', function(){
alert ( 'click' );
});
};
var render = function(){
console.log( '开始渲染列表' );
bindEvent();
};
render();
render();

//下面利用单例模式实现
var bindEvent = getSingle(function(){
document.getElementById( 'div1' ).onclick = function(){
alert ( 'click' );
}
return true;
});

var render = function(){
console.log( '开始渲染列表' );
bindEvent();
};

render();
render();

策略模式

策略模式指的是定义一系 列的算法,把它们一个个封装起来。将不变的部分和变化的部分隔开是每个设计模式的主题,策 略模式也不例外,策略模式的目的就是将算法的使用与算法的实现分离开来。

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
//案例
var calculateBonus = function( performanceLevel, salary ){
if ( performanceLevel === 'S' ){
return salary * 4;
}
if ( performanceLevel === 'A' ){
return salary * 3;
}
if ( performanceLevel === 'B' ){
return salary * 2;
}
};

calculateBonus( 'B', 20000 ); // 输出:40000
calculateBonus( 'S', 6000 ); // 输出:24000

//使用策略模式
var strategies = {
"S": function( salary ){
return salary * 4;
},
"A": function( salary ){
return salary * 3;
},
"B": function( salary ){
return salary * 2;
}
};
var calculateBonus = function( level, salary ){
return strategies[ level ]( salary );
};
console.log( calculateBonus( 'S', 20000 ) ); // 输出:80000
console.log( calculateBonus( 'A', 10000 ) ); // 输出:30000

代理模式

代理模式包括许多小分类,在 JavaScript 开发中最常用的是虚拟代理和缓存代理

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
//不用代理模式  下面代码只能实现预加载, 如果哪天不需要预加载了 整个代码都需要改变
var MyImage = (function(){
var imgNode = document.createElement( 'img' );
document.body.appendChild( imgNode );
var img = new Image;
img.onload = function(){
imgNode.src = img.src;
};
return {
setSrc: function( src ){
imgNode.src = 'file:// /C:/Users/svenzeng/Desktop/loading.gif';
img.src = src;
}
}
})();

MyImage.setSrc( 'http:// imgcache.qq.com/music/photo/k/000GGDys0yA0Nk.jpg' );

//使用代理模式
var myImage = (function(){
var imgNode = document.createElement( 'img' );
document.body.appendChild( imgNode );
return {
setSrc: function( src ){
imgNode.src = src; }
}
})();
var proxyImage = (function(){
var img = new Image;
img.onload = function(){
myImage.setSrc( this.src );
}
return {
setSrc: function( src ){
myImage.setSrc( 'file:// /C:/Users/svenzeng/Desktop/loading.gif' );
img.src = src;
}
}
})();

proxyImage.setSrc( 'http:// imgcache.qq.com/music/photo/k/000GGDys0yA0Nk.jpg' );

缓存代理

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
/**************** 计算乘积 *****************/ 
var mult = function(){
var a = 1;
for (var i = 0, l = arguments.length; i < l; i ++) {
a *= arguments[i];
}

return a;
}
/**************** 计算加和 *****************/
var plus = function(){
var a = 1;
for (var i = 0, l = arguments.length; i < l; i ++) {
a += arguments[i];
}

return a;
}

/**************** 创建缓存代理的工厂 *****************/
var createProxyFactory = function( fn ){
var cache = {};
return function(){
var args = Array.prototype.join.call( arguments, ',' );
if ( args in cache ){
return cache[ args ];
}
return cache[ args ] = fn.apply( this, arguments );
}
};

var proxyMult = createProxyFactory( mult ),
proxyPlus = createProxyFactory( plus );

alert ( proxyMult( 1, 2, 3, 4 ) );//输出:24
alert ( proxyPlus( 1, 2, 3, 4 ) );//输出:10

状态模式

传统的状态模式

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
178
179
180
window.external.upload = function(state) {
console.log(state);
}

var plugin = (function() {
var plugin = document.createElement( 'embed' );
plugin.style.display = 'none';
plugin.type = 'application/txftn-webkit';

plugin.sign = function() {
console.log("开始文件扫描");
}

plugin.pause = function() {
console.log("暂停文件扫描");
}

plugin.uploading = function() {
console.log("开始文件上传");
}

plugin.del = function() {
console.log("删除文件上传");
}

plugin.done = function() {
console.log("文件上传完成");
}

document.body.appendChild( plugin );
return plugin;
})();

var Upload = function( fileName ){
this.plugin = plugin;
this.fileName = fileName;
this.button1 = null;
this.button2 = null;
this.signState = new SignState( this );
this.uploadingState = new UploadingState( this );
this.pauseState = new PauseState(this);
this.doneState = new DoneState(this);
this.errorState = new ErrorState(this);
this.currState = this.signState;
}

Upload.prototype.init = function(){
var that = this;
this.dom = document.createElement( 'div' );
this.dom.innerHTML = '<span>文件名称:' + this.fileName +'</span> \
<button data-action="button1">扫描中</button> \
<button data-action="button2">删除</button>';

document.body.appendChild( this.dom );
this.button1 = this.dom.querySelector( '[data-action="button1"]' );
this.button2 = this.dom.querySelector( '[data-action="button2"]' );
this.bindEvent();
};

Upload.prototype.bindEvent = function(){
var self = this;
this.button1.onclick = function(){
self.currState.clickHandler1();
}
this.button2.onclick = function(){
self.currState.clickHandler2();
}
};

Upload.prototype.sign = function(){
this.plugin.sign();
this.currState = this.signState;
};

Upload.prototype.uploading = function(){
this.button1.innerHTML = '正在上传, 点击暂停';
this.plugin.uploading();
this.currState = this.uploadingState;
};

Upload.prototype.pause = function(){
this.button1.innerHTML = '已暂停, 点击继续上传';
this.plugin.pause();
this.currState = this.pauseState;
};

Upload.prototype.done = function(){
this.button1.innerHTML = '上传完成';
this.plugin.done();
this.currState = this.doneState;
};

Upload.prototype.error = function(){
this.button1.innerHTML = '上传失败';
this.currState = this.errorState;
};

Upload.prototype.del = function(){
this.plugin.del();
this.dom.parentNode.removeChild( this.dom );
};

var StateFactory = (function() {
var State = function() {};

State.prototype.clickHandler1 = function() {
throw new Error("子类必须重写父类的clickHandler1 方法");
}
State.prototype.clickHandler2 = function() {
throw new Error("子类必须重写父类的clickHandler2 方法");
}

return function(param) {
var F = function( uploadObj ){
this.uploadObj = uploadObj;
};
F.prototype = new State();
for(var i in param){
F.prototype[ i ] = param[ i ];
}
return F;
}
})
var SignState = StateFactory({
clickHandler1: function(){
console.log( '扫描中, 点击无效...' );
},
clickHandler2: function(){
console.log( '文件正在上传中, 不能删除' );
}
});

var UploadingState = StateFactory({
clickHandler1: function(){
this.uploadObj.pause();
},
clickHandler2: function(){
console.log( '文件正在上传中, 不能删除' );
}
});

var PauseState = StateFactory({
clickHandler1: function(){
this.uploadObj.uploading();
},
clickHandler2: function(){
this.uploadObj.del();
}
});
var DoneState = StateFactory({
clickHandler1: function(){
console.log( '文件上传上传, 点击无效' );
},
clickHandler2: function(){
this.uploadObj.del();
}
});
var ErrorState = StateFactory({
clickHandler1: function(){
console.log( '文件上传失败, 点击无效' );
},
clickHandler2: function(){
this.uploadObj.del();
}
});

var uploadObj = new Upload( 'JavaScript ' );
uploadObj.init();
window.external.upload = function( state ){
uploadObj[ state ]();
};
window.external.upload( 'sign' );

setTimeout(function(){
window.external.upload( 'uploading' );
}, 1000 );

setTimeout(function(){
window.external.upload( 'done' );
}, 5000 );

JavaScript版本的状态模式

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
var delegate = function(client, delegation) {
return {
buttonWasPressed: function() {
return delegation.buttonWasPressed.apply(client, arguments);
}
}
}

var FSM = {
off: {
buttonWasPressed: function() {
console.log("关灯");
this.button.innerHTML = "下一次按我是开灯";
this.currState = this.onState;
}
},
on: {
buttonWasPressed: function() {
console.log("开灯");
this.button.innerHTML = "下一次按我是关灯";
this.currState = this.offState;
}
}
}

var Light = function() {
this.offState = delegate(this, FSM.off);
this.onState = delegate(this, FSM.on);
this.currState = this.offState;
this.button = null;
}

Light.prototype.init = function() {
var button = document.createElement("button"),
self = this;
button.innerHTML = "已关灯";
this.button = document.body.applyChild(button);
this.button.onclick = function() {
self.currState.buttonWasPressed();
}
};

var light = new Light();
light.init();

cpp提高

模板<泛型>

cpp编译器对模板进行两次编译, 根据程序中用到的类型生成对应的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template <typename T>
void mySwap(T &a, T &b) {
a = a ^ b;
b = a ^ b;
a = a ^ b;
}

int main(int argc, const char * argv[]) {
// insert code here...
int a = 1;
int b = 2;
char c = 'c';
char d = 'd';

mySwap<int>(a, b);
cout << " a: " << a << " b: " << b << endl;
mySwap<char>(c, d);
cout << " c: " << c << " d: " << d << endl;
return 0;
}

不同类型静态变量

不同类型的静态变量 各分配内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
template <typename T>
class TestA {
public:
static T a;
};

//这句必须要申明
template <typename T>
T TestA<T>::a = 0;

int main(int argc, const char * argv[]) {
// insert code here...
TestA<int> a;
a.a = 10;
TestA<char> b;
b.a = 'a';

cout << "TestA<int>::a " << TestA<int>::a << " TestA<char>::a " << TestA<char>::a << endl;//TestA<int>::a 10 TestA<char>::a a

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

template <typename T>
class MyVertor2 {
friend ostream& operator<<(ostream& out, const MyVertor2<T> &obj)
{

out << "开始重装 <<: " ;
int len = obj.getLen();
for (int i = 0; i < len; i++) {
out << obj.m_space[i] << " ";
}

out << endl;

return out;
}

protected:
int m_len;
T* m_space;
public:
MyVertor2(int len = 0);
MyVertor2(const MyVertor2& obj);
~MyVertor2();
public:
T& operator[](int index);
MyVertor2& operator=(const MyVertor2& obj);
int getLen()const;
};

template <typename T>
MyVertor2<T>::MyVertor2(int len)
{
this->m_len = len;
this->m_space = new T[len];
}

template <typename T>
MyVertor2<T>::MyVertor2(const MyVertor2& obj)
{
m_len = obj.m_len;
m_space = new T[m_len];

for (int i = 0; i < m_len; i++) {
m_space[i] = obj.m_space[i];
}

return *this;
}

template <typename T>
MyVertor2<T>::~MyVertor2()
{
if (m_space != NULL) {
delete [] m_space;
m_space = NULL;
m_len = 0;
}
}


template <typename T>
T& MyVertor2<T>::operator[](int index)
{
return m_space[index];
}

template <typename T>
MyVertor2<T>& MyVertor2<T>::operator=(const MyVertor2& obj)
{
if (m_space != NULL) {
delete [] m_space;
m_space = NULL;
m_len = 0;
}

m_len = obj.m_len;
m_space = new T[m_len];

for (int i = 0; i < m_len; i++) {
m_space[i] = obj.m_space[i];
}

return *this;
}

template <typename T>
int MyVertor2<T>::getLen() const
{
return m_len;
}
class Human {

friend ostream& operator<<(ostream& out, const Human& obj) {
out << "name: " << obj.m_name << " age:" << obj.m_age << endl;
return out;
}
protected:
char* m_name;
int m_age;
public:
Human() {
m_age = 18;
m_name = new char[0];
strcpy(m_name, "");
}
Human(char *p_name, int age) {
int len = strlen(p_name) + 1;
m_name = new char[len];
strcpy(m_name, p_name);
m_age = age;
}
~Human() {
if (m_name != NULL) {
delete m_name;
m_name = NULL;
}
}
Human& operator=(const Human& obj) {
if (m_name != NULL) {
delete m_name;
m_name = NULL;
}

int len = strlen(obj.m_name) + 1;
m_name = new char[len];
strcpy(m_name, obj.m_name);

m_age = obj.m_age;
return *this;
}
};

int main(int argc, const char * argv[]) {
Human h1("h1", 1);
Human h2("h2", 2);
MyVertor2<Human> v1(1);
v1[0] = h1;

MyVertor2<Human> v2;
v2 = v1;


cout << v2[0];

return 0;
}

强制类型转换

  • static_cast 同类型转换(编译时进行类型检测), 比如int->long
  • reinterpret_cast 不同类型之间转换 可以转换任意一个32bit整数,包括所有的指针和整数
  • dynamic_cast 多态之间转换
  • const_cast 去除只读属性
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
void modifyP(const char *p) {
char *p1 = NULL;
p1 = const_cast<char *>(p);

p1[0] = 'A';
}

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

char buf[10] = "aaaaaaaaa";

modifyP(buf);

cout << "buf:" << buf << endl;

int a = 1;
double b;
char c;
int* p = NULL;

// p = static_cast<int *>(a);
p = reinterpret_cast<int *>(a);

cout << "p: " << p << 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
class ExpObj {
public:
ExpObj() {
cout << "构造函数" << endl;
}
~ExpObj() {
cout << "析构函数" << endl;
}
};

// throw是限定抛出什么类型的异常
void TestThrow() throw(int, char, ExpObj)
{
throw ExpObj();
}

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

try {
TestThrow();
} catch (char *e) {
cout << "throw char :" << e << endl;
} catch (int e) {
cout << "throw int :" << e << endl;
} catch (ExpObj &e) { //如果不用引用来接受 会进行拷贝对象
cout << "throw ExpObj :" << endl;
} catch (...) {
cout << "unkown exp" << 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
class MyExp : public exception {
public:
MyExp(char * p) {
this->m_p = p;
}
virtual const char * what() {
cout << "MyExp: " << m_p << endl;
return m_p;
}
private:
char* m_p;
};

void testMyExp() {
throw MyExp("异常抛出");
}

int main(int argc, const char * argv[]) {
try {
testMyExp();
} catch (MyExp &e) {
e.what();
}
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
class TestThrowClass {
public:
class expParent{
public:
virtual void printfExp() {
cout << "expParent" << endl;
}
};
class expSub1 : public expParent{
public:
virtual void printfExp() {
cout << "expSub1" << endl;
}
};
class expSub2 : public expParent{
public:
virtual void printfExp() {
cout << "expSub2" << endl;
}
};

void testMethod(int num) {
if (num < 0) {
throw expSub1();
} else if (num > 0) {
throw expSub2();
} else {
throw expParent();
}
}
};

int main(int argc, const char * argv[]) {
TestThrowClass t;
try {
t.testMethod(-1);

} catch (TestThrowClass::expParent &e) {
e.printfExp();
}
return 0;
}

IO

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
#include "fstream"
class Teacher {
public:
Teacher() {
_age = 18;
strcpy(_name, "");
}
Teacher(char *name, int age) {
_age = age;
strcpy(_name, name);
}
void printInfo() {
cout << "name: " << _name << " age: " << _age << endl;
}
private:
int _age;
char _name[32];
};
int main(int argc, const char * argv[]) {
char *fname = "/Users/xxxx/Desktop/test.txt";
ofstream fout(fname, ios::binary);
if (!fout) {
cout << "open file error";
return -1;
}
Teacher t1("t1", 18);
Teacher t2("t2", 19);
fout.write((char*)&t1, sizeof(Teacher));
fout.write((char*)&t2, sizeof(Teacher));
fout.close();

Teacher tmp;
ifstream fin(fname);
fin.read((char *)&tmp, sizeof(Teacher));
tmp.printInfo();
fin.close();
return 0;
}

STL

vector

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 "vector"
class Teacher {
public:
Teacher() {
_age = 18;
strcpy(_name, "");
}
Teacher(char *name, int age) {
_age = age;
strcpy(_name, name);
}
void printInfo() {
cout << "name: " << _name << " age: " << _age << endl;
}
private:
int _age;
char _name[32];
}
void TestVector() {
Teacher t1("t1", 18);
Teacher t2("t2", 19);
vector<Teacher> v;
v.push_back(t1);
v.push_back(t2);

for (vector<Teacher>::iterator it = v.begin(); it != v.end(); it++) {
it->printInfo();
}
}

int main(int argc, const char * argv[]) {
TestVector();
return 0;
}

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
#include "string"
#include "algorithm"
void TestString() {
string s1 = "hello world hello world hello world hello world ";
string str = "hello";
string str2 = "HELLO";
int strlen = str2.length();
int index = 0;
while ((index = s1.find(str, index)) != string::npos) {
s1.replace(index, strlen, str2);
index += strlen;
}
cout << "s1: " << s1 << endl;

str.append(" world");
transform(str.begin(), str.end(), str.begin(), ::toupper);
cout << "str: " << str << endl;

str2.insert(0, "nihao, ");
str2.insert(str2.length(), "!!!");
transform(str2.begin(), str2.end(), str2.begin(), ::tolower);
cout << "str2: " << str2 << endl;
}
int main(int argc, const char * argv[]) {
TestString();
return 0;
}

list

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
#include "list"
void printList(list<int> l) {
for (list<int>::iterator it = l.begin(); it != l.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
void TestList() {
list<int> l;
for (int i = 0; i < 10; i++) {
l.push_back(i);
}
printList(l);

l.insert(l.begin(), -1);
l.insert(l.begin(), -2);
l.insert(l.begin(), -3);
printList(l);

l.erase(l.begin());
printList(l);

list<int>::iterator it1 = l.begin();
list<int>::iterator it2 = l.begin();
it2 ++;
it2 ++; //必须一次次叠加不能直接+2
l.erase(it1, it2);
printList(l);

l.remove(5);
printList(l);
}
int main(int argc, const char * argv[]) {
TestList();
return 0;
}

queue

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
#include "queue"
void fillQueue(priority_queue<int> &q) {
q.push(1);
q.push(10);
q.push(4);
q.push(8);
}
void fillQueue2(priority_queue<int, vector<int>, less<int>> &q) {
q.push(1);
q.push(10);
q.push(4);
q.push(8);
}
void fillQueue3(priority_queue<int, vector<int>, greater<int>> &q) {
q.push(1);
q.push(10);
q.push(4);
q.push(8);
}

void printQueue(priority_queue<int> q) {
while (!q.empty()) {
int tmp = q.top();
cout << tmp << " ";
q.pop();
}
cout << endl;
}
void printQueue2(priority_queue<int, vector<int>, less<int>> q) {
while (!q.empty()) {
int tmp = q.top();
cout << tmp << " ";
q.pop();
}
cout << endl;
}
void printQueue3(priority_queue<int, vector<int>, greater<int>> q) {
while (!q.empty()) {
int tmp = q.top();
cout << tmp << " ";
q.pop();
}
cout << endl;
}
void TestQueue() {
priority_queue<int> q1;//默认最大值优先队列
priority_queue<int, vector<int>, less<int>> q2; //最大值优先队列
priority_queue<int, vector<int>, greater<int> > q3;//最小值优先队列

fillQueue(q1);
fillQueue2(q2);
fillQueue3(q3);

printQueue(q1);
printQueue2(q2);
printQueue3(q3);
}
int main(int argc, const char * argv[]) {
TestQueue();
return 0;
}

set/multiset

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
#include "set"
class Teacher {
public:
Teacher() {
_age = 18;
strcpy(_name, "");
}
Teacher(char *name, int age) {
_age = age;
strcpy(_name, name);
}
void printInfo() {
cout << "name: " << _name << " age: " << _age << endl;
}
int getAge() const {
return _age;
}
const char* getName() const {
return _name;
}
private:
int _age;
char _name[32];
};
struct CustOrder {
bool operator()(const Teacher& left, const Teacher& right) {
return left.getAge() < right.getAge();
}
};
void insertSet(set<Teacher, CustOrder>&s, Teacher &t) {
pair<set<Teacher, CustOrder>::iterator, bool> pair = s.insert(t);
if (pair.second == true) {
cout << t.getName() << " 插入成功" << endl;
} else {
cout << t.getName() << " 插入失败" << endl;

}
}
void TestSet() {
Teacher t1("t1", 18);
Teacher t2("t2", 38);
Teacher t3("t3", 28);
Teacher t4("t4", 8);
Teacher t5("t5", 18);
Teacher t6("t6", 58);
set<Teacher, CustOrder> s1;
insertSet(s1, t1);
insertSet(s1, t2);
insertSet(s1, t3);
insertSet(s1, t4);
insertSet(s1, t5);
insertSet(s1, t6);

for (set<Teacher, CustOrder>::iterator it = s1.begin(); it != s1.end(); it++) {
cout << "name: " << it->getName() << " age:" << it->getAge() << endl;
}
}
int main(int argc, const char * argv[]) {
TestSet();
return 0;
}

map/multimap

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
#include "map"
#include "string"

void TestMap() {
map<string, int> m1;
m1.insert(pair<string, int>("m1", 18));
m1.insert(make_pair("m2", 19));
m1.insert(map<string, int>::value_type("m3", 20));
m1["m4"] = 21;

while (!m1.empty()) {
map<string, int>::iterator it = m1.begin();
cout << "name: " << it->first << " age: " << it->second << endl;
m1.erase(it);
}


Teacher t1("t1", 18);
Teacher t2("t2", 19);
Teacher t3("t3", 20);
Teacher t4("t4", 21);
multimap<string, Teacher> m2;
m2.insert(make_pair("js", t1));
m2.insert(make_pair("js", t2));
m2.insert(make_pair("cpp", t3));
m2.insert(make_pair("cpp", t4));

for (multimap<string, Teacher>::iterator it = m2.begin(); it != m2.end(); it ++) {
cout << "type: " << it->first << " info: name:" << it->second.getName() << " age: " << it->second.getAge() << endl;
}
}

int main(int argc, const char * argv[]) {
TestMap();
return 0;
}

算法

for_each

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
template <typename T>
class ShowElement {
public:
int num = 0;
void operator()(T &t) {
cout << t << endl;
num++;
}

void showNum() {
cout << "num: " << num << endl;
}
};

template <typename T>
void showElementFunc(T &t) {
cout << t << endl;
}

void testCallBack() { vector<int> v;
v.push_back(1);
v.push_back(3);
v.push_back(5);

for_each(v.begin(), v.end(), ShowElement<int>());
for_each(v.begin(), v.end(), showElementFunc<int>);

//for_each函数是值传递不是引用传递
ShowElement<int> showElement;
ShowElement<int> showElement2 = for_each(v.begin(), v.end(), showElement);
showElement.showNum(); //0
showElement2.showNum();//3
}

int main(int argc, const char * argv[]) {
testCallBack();
return 0;
}

谓词/find_if

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
template <typename T>
class IsDiv {
public:
T div;
IsDiv(T &t) {
div = t;
}

bool operator()(T &t) {
return (t % div == 0);
}
};

void testPred() {
vector<int> v;
for (int i = 33; i <= 66; i++) {
v.push_back(i);
}

int div = 5;

vector<int>::iterator it = find_if(v.begin(), v.end(), IsDiv<int>(div));
if (it == v.end()) {
cout << "没有找到能被 " << div << " 整除的数" << endl;
} else {
cout << "能被 " << div << " 整除的数是 " << *it << endl;
}
}

int main(int argc, const char * argv[]) {
testPred();
return 0;
}

functional

for_each 的回调函数返回值可以是void, transform 必须有返回值

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 "functional"
int increase(int num) {
return num + 10;
}
template <typename T>
void printV(vector<T> v) {
cout << "vector: ";
for (int i = 0; i < v.size(); i++) {
cout << v[i] << " ";
}
cout << endl;
}
void testFunc() {
vector<int> v;
for (int i = 0; i < 10; i ++) {
v.push_back(i);
}

int num = 4;
int count = count_if(v.begin(), v.end(), bind2nd(greater<int>(), num));
cout << "大于 " << num << " 的个数为 " << count << endl;

count = count_if(v.begin(), v.end(), bind2nd(modulus<int>(), num));
cout << "能被 " << num << " 整除的个数为 " << count << endl;

count = count_if(v.begin(), v.end(), not1(bind2nd(modulus<int>(), num)));
cout << "不能被 " << num << " 整除的个数为 " << count << endl;

transform(v.begin(), v.end(), v.begin(), increase);
printV(v);
}
int main(int argc, const char * argv[]) {
testFunc();
return 0;
}

cpp

引用

别名引用

别名引用实质就是一个常量指针 Type& variable == Type* const variable

1
2
3
4
5
6
7
8
9
10
11
int main(int argc, const char * argv[]) {
int a = 10;
int &b = a;

a = 20;

std::cout << "a: " << a << std::endl; //20
std::cout << "b: " << b << std::endl; // 20

return 0;
}

引用可做函数返回值

1
2
3
4
5
6
7
8
9
10
11
int& getA() {
static int a = 1;
cout << "a: " << a << endl;
return a;
}

int main(int argc, const char * argv[]) {
getA() = 10;
getA() = 100;
return 0;
}

常量引用

const Type& variable == const Type* const variable

1
2
3
4
5
6
7
8
9
10
int main(int argc, const char * argv[]) {
int a = 1;
const int &b = a;
const int &c = 1;
a = 10;
// b = 100; //报错
cout << "a: " << a << " b: " << b << " c:" << c << endl;

return 0;
}

函数

inline内联函数

inline函数可以减少函数进栈出栈所消耗的性能, 一般用于一些简单的逻辑

define与inline函数的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define FUNC(a, b) ((a) < (b) ? (a) : (b))
inline int FUNC2(int a, int b) {
return a < b ? a : b;
}

int main(int argc, const char * argv[]) {
int a = 1;
int b = 3;
// int c = FUNC(++a, b); // ((a++) < (b) ? (a++) : (b)) a :3 b:3 c: 3
// cout << "a :" << a << " b:" << b << " c: " << c << endl;


int c = FUNC2(++a, b); //((a++) < (b) ? (a) : (b)) a :2 b:3 c: 2
cout << "a :" << a << " b:" << b << " c: " << c << 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
class myClass {
int _age;
public:
myClass() {
std::cout << "无参构造";
}
myClass(int age) {
_age = age;
}
myClass(const myClass &obj) {
_age = obj._age;
}

~myClass() {
std::cout << "我是析构方法" << std::endl;
}
void myClass::setAge(int age){
_age = age;
}
int getAge() {
return _age;
}
};
int main(int argc, const char * argv[]) {
myClass c(18);
myClass d = c;
myClass e;
cout << "age: " << c.getAge() <<endl;
cout << "age: " << d.getAge() <<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
class c1 {
public:
c1(int a) {
cout << "c1 构造方法 " << endl;
}
~c1() {
cout << "c1 析构方法" << endl;
}
};
class c2 {
public:
c1 a1;
c1 a2;
c2(int m1, int m2, int m3) : a1(m2), a2(m3)
{
cout << "c2 构造方法 " << endl;
}
~c2() {
cout << "c2 析构方法" << endl;
}
};

int main(int argc, const char * argv[]) {
c2 a2(1, 2, 3);
/**
c1 构造方法
c1 构造方法
c2 构造方法
c2 析构方法
c1 析构方法
c1 析构方法
**/
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
class c3 {
public:
int _a;
int _b;
int _c;
c3(int a, int b) {
cout << "2个参数构造方法" << endl;
c3(a, b, 100);
}
c3 (int a, int b, int c) {
cout << "3个参数构造方法" << endl;
_a = a;
_b = b;
_c = c;
}

void printParmas() {
cout << "_a: " << _a << " _b: " << _b << " _c: " << _c << endl;
}

~c3() {
cout << "析构方法被调用" << endl;
}
};
int main(int argc, const char * argv[]) {
c3 c(1, 2);
c.printParmas();
/***
2个参数构造方法
3个参数构造方法
析构方法被调用
_a: 0 _b: 0 _c: 0
析构方法被调用
**/
return 0;
}

类中的static变量

同一个类的所有对象公用同一个static变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class c4 {
public:
int a = 0;
static int b;
void coutParams() {
cout << "a: " << a << " b: " << b << endl;
}
void addParams() {
a ++;
b ++;
}
};
int c4::b = 0; //必须在外面声明
int main(int argc, const char * argv[]) {
c4 c1, c2;
c1.coutParams();
c2.coutParams();
c1.addParams();
c2.addParams();
c1.coutParams();
c2.coutParams();
return 0;
}

类中的static方法

static方法中只能使用static变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class c5 {
public:
int a = 0;
static int b;
//static方法中只能使用static变量
static void coutParams() {
cout << " b: " << b << endl;
}
void addParams() {
a ++;
b ++;
}
};
int c5::b = 0;
int main(int argc, const char * argv[]) {
c5 c1, c2;
c1.coutParams();
c1.addParams();
c5::coutParams();
c1.coutParams();
return 0;
}

类的大小

下面这个类对象只占4字节而不是16字节, 他们是怎么分配内存的呢? 其实c5是用结构体实现的, 只有a变量是放在结构体c1的内存里, 静态变量b和静态方法coutParams都放在全局内存区, addParams放在代码内存区;
那么问题来了, 那调用addParams时如何区别是哪个对象调用的呢? 其实cpp编译会将当前对象以一个变量名为this的变量传递给方法, 因此这也是为什么我们能再方法内使用this变量来调用对象的变量和方法

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
class c5 {
public:
int a = 0;
static int b;
static void coutParams() {
cout << " b: " << b << endl;
}
void addParams() {
a ++;
b ++;
}
};
int c5::b = 0;
int main(int argc, const char * argv[]) {
c5 c1;

cout << "c1 size: " << sizeof(c1) << endl; // 4
return 0;
}


// c5转换为c代码是如下
struct c5{
int a = 0;
}
//代码区
void addParams(const c5 this) {
a ++;
b ++;
}
//静态区
static int b;
static void coutParams() {
cout << " b: " << b << endl;
}

new和delete

new/delete与malloc/free 功能类似, 都是向堆区申请内存, 并且可以混用, 但是new会调用类的构造方法, delete会调用类的析构方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main(int argc, const char * argv[]) {
int * p1 = new int(11);
char * p2 = new char[100];
strcpy(p2, "hello world");
c3* p3 = new c3(1, 2);
cout << "p1: " << *p1 << endl;

cout << "p2: " << p2 << endl;

delete p1;
delete []p2;
delete p3;
return 0;
}

类的const

void setA() const ==> void setA(const c1 * const this)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class c1 {
public:
int a;
void setA() const
{
// this->a = 100; 这里会报错 因为有const修饰
}
int getA() {
return this->a;
}
};

int main(int argc, const char * argv[]) {
c1 c;
c.setA();
cout<< " a: " << c.getA() << 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
class C1 {
public:
int a;
C1(int a = 0) {
this->a = a;
}
C1& add(C1 c2) {
this->a = this->a + c2.a;

return *this;
}

void printA() {
cout << " a: " << this->a << endl;
}
};

int main(int argc, const char * argv[]) {
C1 c1(1), c2(2);
c1.add(c2);
c1.printA(); // a: 3
c2.printA(); // a: 2
return 0;
}

友元函数

通过friend申明为友元方法, 可以使用外部函数直接修改类的私有变量

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
class C1 {
private:
int a;
public:
friend void modifyA(C1 &c, int _a);
C1(int a = 0) {
this->a = a;
}
C1& add(C1 c2) {
this->a = this->a + c2.a;

return *this;
}

void printA() {
cout << " a: " << this->a << endl;
}
};

void modifyA(C1 &c, int _a) {
c.a = _a;
}

int main(int argc, const char * argv[]) {
C1 c1(1);
c1.printA();
modifyA(c1, 100);
c1.printA();
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
class C1 {
private:
int a;
public:
friend class C2;
C1(int a = 0) {
this->a = a;
}
C1& add(C1 c2) {
this->a = this->a + c2.a;

return *this;
}

void printA() {
cout << " a: " << this->a << endl;
}
};

class C2 {
private:
C1 c;
public:
void setC1A(int _a) {
c.a = _a;
}
void printA() {
cout << " a: " << c.a << endl;
}
};

int main(int argc, const char * argv[]) {
C2 c2;
c2.printA();
c2.setC1A(100);
c2.printA();
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
class Human {
public:
virtual void introduce() {
cout << " I am Human" << endl;
}
void echo(char* p, int a) {
cout << "echo: " << p << " a: " << a << endl;
}
virtual ~Human() {
cout << "Human 析构函数" << endl;
}
};

class Man : public Human {
public:
void introduce() {
cout << " I am Man " << endl;
}
void echo(char* p) {
cout << "echo: " << p << endl;
}
~Man() {
cout << "Man 析构函数" << endl;
}
};

class Woman : public Human {
public:
void introduce() {
cout << " I am Human " << endl;
}
~Woman() {
cout << "Woman 析构函数" << endl;
}
};

int main(int argc, const char * argv[]) {
Human *human = NULL;
Man man = Man();
Woman woman = Woman();
human = &man;

man.echo("hello");
man.Human::echo("hello", 1);//无法直接调用man.echo("hello", 1)

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

class C3 {
int* arr;
int a = 0;
//因为是私有变量所以需要将全局函数声明为友元函数
friend C3 operator+(C3 &c1, C3 &c2);
friend C3 operator++(C3 &c1, int);
public:
C3(int a) {
this->a = a;
this->arr = new int[100];
}

void printA() {
cout << "a: " << this->a << endl;
}

//第一种 用类方法声明
C3 operator-(C3 &c2) {
C3 tmp(0);
tmp.a = this->a - c2.a;

return tmp;
}

//一元操作符重载
C3& operator++() {
this->a++;

return *this;
}

//后置--
C3 operator--(int) {
C3 tmp = *this;
this->a--;

return tmp;
}

int& operator[](int i) {
this->arr[i] = i;

return i;
}
};
//第二种 使用全局函数声明
C3 operator+(C3 &c1, C3 &c2) {
C3 tmp(0);
tmp.a = c1.a + c2.a;

return tmp;
}
C3 operator++(C3 &c1, int){
C3 tmp = c1;
c1.a ++;
return tmp;
}
int main(int argc, const char * argv[]) {
C3 c1(1), c2(2);

C3 c3 = c1 + c2;
c3.printA();
C3 c4 = c1 - c2;
c4.printA();
++c4;
c4.printA();
for (int i = 0; i < 5; i ++) {
c4[i] = i;
cout << c4[i] << endl;
}
return 0;
}

类继承

类继承也有三种修饰符public, protected, private

  • public继承, 就是默认继承
  • protected继承, public属性和方法都会变成protected
  • private继承, public, protected属性和方法都会变成private

    属性的重写

    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
    class Parent {
    public:
    int a;
    protected:
    int b;
    private:
    int c;
    public:
    void printAll() {
    cout << "a: " << a << " b:" << b << " c:" << c << endl;
    }
    };
    class Son : public Parent{
    public:
    int b;
    void printAll2() {
    cout << "a: " << a << " b:" << b << endl;
    }
    };

    int main(int argc, const char * argv[]) {
    Son s1;
    s1.a = 1;
    s1.b = 2;
    s1.Parent::a = 2;
    s1.printAll(); //a: 2 b:0 c:0
    s1.printAll2(); //a: 1 b:2

    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
class Object {
public:
int o1;
int o2;
Object(int o1, int o2) {
this->o1 = o1;
this->o2 = o2;
cout << "Object 构造方法: o1: " << o1 << " o2: " << o2 << endl;
}
~Object() {
cout << "Object 析构方法: o1: " << o1 << " o2: " << o2 << endl;
}
};
class Parent: public Object {
public:
int a;
protected:
int b;
private:
int c;
public:
Parent(int a, int b) : Object(a + 1, b + 1) {
this->a = a;
this->b = b;
cout << "Parent 构造方法: a: " << a << " b: " << b << endl;
}
~Parent() {
cout << "Parent 析构方法: a: " << a << " b: " << b << endl;
}
void printAll() {
cout << "a: " << a << " b:" << b << " c:" << c << endl;
}
};
class Son : public Parent{
public:
int b;
Object o1;
Object o2;
Son(int a, int b) : o2(a, 2), Parent(a+1, b + 1), o1(a, b) {
cout << "Son 构造方法: a: " << a << " b: " << b << endl;
}
~Son() {
cout << "Son 析构方法" << endl;
}
void printAll2() {
cout << "a: " << a << " b:" << b << endl;
}
};

int main(int argc, const char * argv[]) {
Son s1(1, 2);
/**
Object 构造方法: o1: 3 o2: 4
Parent 构造方法: a: 2 b: 3
Object 构造方法: o1: 1 o2: 2
Object 构造方法: o1: 1 o2: 2
Son 构造方法: a: 1 b: 2
Son 析构方法
Object 析构方法: o1: 1 o2: 2
Object 析构方法: o1: 1 o2: 2
Parent 析构方法: a: 2 b: 3
Object 析构方法: o1: 3 o2: 4
**/
return 0;
}

虚继承与继承二义性

如下, B1与B2都继承了A, C又同时继承了B1和B2, 这样就产生了二义性,
为了防止产生二义性应该给B1, B2添加virtual, 表示虚继承, 这样A的构造函数只会被执行一次,
虚继承会给类多加了8字节的数据;
如果在2个类中出现同样名称的变量, 只能通过 obj.class::var的方式来解决二义性

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
cclass A {
public:
int a;
A() {
cout << "I am A" << endl;
}
};

class B1 : virtual public A {
public:
int b;

};

class B2 : virtual public A {
public:
int b;
};

class C4: public B1, public B2 {
public:
int c;
};

int main(int argc, const char * argv[]) {
C4 c;
c.a = 1;
c.B1::b = 2;
c.B2::b = 3;
cout << "sizeof(A): " << sizeof(A) << endl;
cout << "sizeof(B1): " << sizeof(B1) << endl;
cout << "sizeof(B2): " << sizeof(B2) << endl;
cout << "sizeof(C4): " << sizeof(C4) << endl;
/**
I am A
sizeof(A): 4
sizeof(B1): 16
sizeof(B2): 16
sizeof(C4): 40
/*
return 0;
}

多态

想让方法实现多态, 必须给方法添加上virtual, 否则使用多态调用时, 只会调用父类的方法, 而不是自己的方法;
没加virtual, 是静态联编, 就是说调用哪个方法, 在编译阶段已经确定;
加了virtual, 是动态联编, 就是说调用哪个方法, 只调用的时候的确定

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
class Human {
public:
virtual void introduce() {
cout << " I am Human" << endl;
}
};

class Man : public Human {
public:
void introduce() {
cout << " I am Man " << endl;
}
};

class Woman : public Human {
public:
void introduce() {
cout << " I am Human " << endl;
}
};

void introduce(Human *p) {
p->introduce();
}
int main(int argc, const char * argv[]) {
Human *human = NULL;
Man man = Man();
Woman woman = Woman();
human = &man;

introduce(&man);
introduce(&woman);
introduce(human);
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
//第一种
typedef void (*myFunc)(int a, int b);
//void likeMultiPoly(myFunc p, int a, int b) {
// 第二种
void likeMultiPoly(void (*p)(int a, int b) , int a, int b) {
p(a, b);
}
void func2(int a, int b) {
printf("func2 a: %d, b: %d\n", a, b);
}
void func3(int a, int b) {
printf("func3 a: %d, b: %d\n", a, b);
}

void func4(int a, int b) {
printf("func4 a: %d, b: %d\n", a, b);
}
int main(int argc, const char * argv[]) {

likeMultiPoly(func2, 1, 2);
likeMultiPoly(func3, 3, 4);
likeMultiPoly(func4, 5, 6);
return 0;
}

虚析构函数

如果通过多态的方式, 释放对象内存, 必须给析构函数加上vitrual, 否则只会调用父类的析构函数, 子类的析构函数不会被调用, 而导致内存溢出

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
class Human {
public:
virtual void introduce() {
cout << " I am Human" << endl;
}
virtual ~Human() {
cout << "Human 析构函数" << endl;
}
};

class Man : public Human {
public:
void introduce() {
cout << " I am Man " << endl;
}
~Man() {
cout << "Man 析构函数" << endl;
}
};

class Woman : public Human {
public:
void introduce() {
cout << " I am Human " << endl;
}
~Woman() {
cout << "Woman 析构函数" << endl;
}
};

void introduce(Human *p) {
p->introduce();
}
void deleteHuman(Human *p) {
delete p;
}
int main(int argc, const char * argv[]) {
Human *human = NULL;
Man man = Man();
Woman woman = Woman();
human = &man;

introduce(&man);
introduce(&woman);
introduce(human);

Human *man2 = new Man();
deleteHuman(man2);
cout << "==========" << endl;
return 0;
}

vptr 指针

cpp的多态是使用vptr指针实现的, 即每个类都会生成一份虚函数映射表, 然后把虚函数放入, 然后通过vptr指针获取对应的虚函数

子类的vptr指针是分步初始化的

子类的vptr指针是在构造函数执行完毕后才初始化完成的

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
class Parent {
public:
Parent() {
print();
}
virtual void print() {
cout << "I am Parent" << endl;
}
};

class Son : public Parent {
public:
Son() {
print();
}
virtual void print() {
cout << "I am Son" << endl;
}
};
int main(int argc, const char * argv[]) {
Son s1;
/**
* I am Parent
* I am Son
*/
return 0;
}

纯虚函数 -> 抽象函数

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
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
class interface1 {
public:
virtual void run() = 0;
virtual void eat() = 0;
};
class interface2 {
public:
virtual void run() = 0;
virtual void echo() = 0;
};

class Human : public interface1, public interface2 {
public:
void run() {
cout << "I am running" << endl;
}
void echo() {
cout << "hello world" << endl;
}
void eat() {
cout << "I like delicious foods" << endl;
}
};
class Pig : public interface1 {
public:
void run() {
cout << "I am running" << endl;
}
void echo() {
cout << "gu gu gu" << endl;
}
void eat() {
cout << "I like any food" << endl;
}
};
class Dog : public interface2 {
public:
void run() {
cout << "I am running" << endl;
}
void echo() {
cout << "Wang Wang Wang" << endl;
}
};
void doSomething(interface1* inter) {
inter->run();
inter->eat();
}
void doSomething2(interface2* inter) {
inter->run();
inter->echo();
}
int main(int argc, const char * argv[]) {
Human h1;
Pig p1;
Dog d1;
doSomething(&h1);
doSomething(&p1);
doSomething2(&h1);
doSomething2(&d1);
return 0;
}