ysbbs

Let's start BB !


  • 首页

  • 归档

  • 关于

  • 标签

有趣的linux下的表达方式vt100

发表于 2018-05-09 | | 阅读次数

现在都是华丽的视窗操作系统的年代了,大家很难想象当年在纯shell环境下的界面效果该如何表达,使用linux的小伙伴可能有意无意的会发现在shell中的命令行和文字的交互中会出现,反显,红色,或者高亮的句子,提示符等,这样可以更鲜艳的在shell下表达信息,使不同信息层次分明的区别开来。

这个使不同字符串以不一样的颜色和状态显示的控制码叫VT100 escape codes,又叫VT100终端编码。

VT100终端编码

使用方法

在C中,

1
2
3
4
5
6
7
#include<stdio.h>
int main(){
printf("\x1B[7m helloword! \033[0m");//反显helloworld
return 0;
}
1
2
或者直接在shell下:
printf("\x1B[7m helloword! \033[0m")

当VT100和C语言结合起来,就可以写出很多丰富多彩的程序,

比如

  • 一个局域网的shell下的聊天室,每个人的聊天语言都可以使用不同的控制码实现不同的颜色。
  • 软件中的互动语句可以使用不同颜色在进行分级,比如 error为红色语句,waring为黄色

常见的VT100控制码:

\033[0m 关闭所有属性

​ \033[1m 设置高亮度

​ \033[4m 下划线

​ \033[5m 闪烁

​ \033[7m 反显

​ \033[8m 消隐

​ \033[30m – \033[37m 设置前景色

​ \033[40m – \033[47m 设置背景色

​ \033[nA 光标上移n 行

​ \033[nB 光标下移n 行

​ \033[nC 光标右移n 行

​ \033[nD 光标左移n 行

​ \033[y;xH设置光标位置

​ \033[2J 清屏

​ \033[K 清除从光标到行尾的内容

​ \033[s 保存光标位置

​ \033[u 恢复光标位置

​ \033[?25l 隐藏光标

​ \033[?25h 显示光标

VT100 关于颜色的说明.
VT100 的颜色输出分为,注意要同时输出前景的字符颜色和背景颜色。

字背景颜色范围:40—-49

​ 40:黑

​ 41:深红

​ 42:绿

​ 43:黄色

​ 44:蓝色

​ 45:紫色

​ 46:深绿

​ 47:白色

​ 字颜色:30———–39

​ 30:黑

​ 31:红

​ 32:绿

​ 33:黄

​ 34:蓝色

​ 35:紫色

​ 36:深绿

​ 37:白色

在linux下打造一个舒适好用的C++环境

发表于 2018-05-09 | | 阅读次数

以Oracle VM VirtualBox虚拟机装Ubuntu为例,主要记录在linux下打造一个舒适好用的C++环境,对于写代码来说已经非常舒服的配置方式了,主要记录软件和vim等配置,实际装机和其它均可以参考。

虚拟机安装配置

Oracle VM VirtualBox 新建linux-Ubuntu,配置的话,默认的1G内存跑16.04的Ubuntu有点吃不消,我电脑12G内存,我给虚拟机4096MB内存,让安装在虚拟机上的系统流畅的跑起来。

  • 虚拟机 设备-网络设置 - 桥接网卡 模式
  • 设备-安装增强模式,添加光碟安装后,弹出光碟,并将剪切板双向和文件双向拖拽勾选上。

安装软件

安装git

1
sudo apt-get install git

安装vim8

1
2
3
sudo add-apt-repository ppa:jonathonf/vim
sudo apt update
sudo apt install vim

vim配置C++ IDE:https://github.com/yangyangwithgnu/use_vim_as_ide ,文章中写的比较乱,但是可以参考。

安装VIM插件管理神器

运行下面命令安装Vundle:

1
git clone https://github.com/gmarik/Vundle.vim.git ~/.vim/bundle/Vundle.vim

Vundle可以帮助我们自动安装卸载vim的插件,配置信息写在vimrc文件中。

vimrc配置文件

为了省事,我并没有把每个配置都单独拿出来解释,这是完全的vim配置文件,到这一步,可以完全复制进使用。

其它插件和vim配置信息:

.vimrc

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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
set nocompatible "be iMproved, required
filetype off " required
" set the runtime path to include Vundle and initialize
set rtp+=~/.vim/bundle/Vundle.vim
call vundle#begin()
" alternatively, pass a path where Vundle should install plugins
"call vundle#begin('~/some/path/here')
" let Vundle manage Vundle, required
Plugin 'VundleVim/Vundle.vim'
Plugin 'scrooloose/syntastic' " 语法检测工具
Plugin 'scrooloose/nerdcommenter' "注释代码工具 \cc注释当前行 \cu 撤销注释当前行 \cs sexily注释 \cA 行尾注释,切换成输入模式
Plugin 'scrooloose/nerdtree' "树状结构文件夹目录
Plugin 'majutsushi/tagbar'
Plugin 'jiangmiao/auto-pairs' " 括号自动匹配
Plugin 'Valloric/YouCompleteMe'
" All of your Plugins must be added before the following line
call vundle#end() " required
filetype plugin indent on " required
" To ignore plugin indent changes, instead use:
" filetype plugin on
"
" Brief help
" :PluginList - lists configured plugins
" :PluginInstall - installs plugins; append `!` to update or just :PluginUpdate
" :PluginSearch foo - searches for foo; append `!` to refresh local cache
" :PluginClean - confirms removal of unused plugins; append `!` to auto-approve removal
"
" see :h vundle for more details or wiki for FAQ
" Put your non-Plugin stuff after this line
syntax on "自动语法高亮"
"用浅色高亮当前行"
autocmd InsertLeave * se nocul
autocmd InsertEnter * se cul
set smartindent "智能对齐"
set autoindent "自动对齐"
set confirm "在处理未保存或只读文件的时候,弹出确认框"
set tabstop=4 "tab键的宽度"
set softtabstop=4 "使得按退格键可以一次删掉4个空格"
set shiftwidth=4 "统一缩进为4"
set expandtab "不要用空格替代制表符"
set autochdir "自动切换当前目录位当前文件所在目录"
set number "显示行号"
set history=50 "历史纪录数"
set hlsearch "搜索时高亮被查找到的文本"
set incsearch "搜素高亮,搜索逐渐高亮"
set hidden " 允许在有未保存的修改时切换缓冲区,此时的修改由 vim 负责保存
set gdefault "行内替换“
set encoding=utf-8
set fileencodings=utf-8,ucs-bom,shift-jis,gb18030,gbk,gb2312,cp936,utf-16,big5,euc-jp,latin1 "编码设置”
set guifont=Menlo:h16:cANSI "设置字体“
set langmenu=zn_CN.UTF-8
set helplang=cn "语言设置”
set ruler "在编辑过程中,在右下角显示光标位置的状态行“
set laststatus=2 " 显示状态栏 (默认值为 1, 无法显示状态栏)
set statusline=\ %<%F[%1*%M%*%n%R%H]%=\ %y\ %0(%{&fileformat}\ %{&encoding}\ %c:%l/%L%)\
" 设置在状态行显示的信息
set showcmd "在状态行显示目前所执行的命令,未完成的指令片段也会显示出来"
set scrolloff=3 "光标移动到buffer的顶部和底部时保持3行的距离"
set showmatch "高亮显示对应的括号"
set matchtime=5 "对应括号高亮时间(单位是十分之一秒)"
set autowrite "在切换buffer时自动保存当前文件"
set wildmenu "增强模式中的命令行自动完成操作"
set linespace=2 "字符间插入的像素行数目"
set whichwrap=b,s,<,>,[,] "开启normal 或visual模式下的backspace键空格键,左右方向键,insert或replace模式下的左方向键,右方向键的跳行功能"
filetype plugin indent on "分为三部分命令:file on,file plugin on,file indent on 分别是自动识别文件类型, 用用文件类型脚本,使用缩进定义文件"
set foldenable "允许折叠"
set foldmethod=syntax " 设置语法折叠
set foldcolumn=0 " 设置折叠区域的宽度
setlocal foldlevel=1 " 设置折叠层数为
" set foldclose=all " 设置为自动关闭折叠
" " nnoremap <space> @=((foldclosed(line('.')) < 0) ? 'zc' : 'zo')<CR>
" " 用空格键来开关折叠
set cursorline "突出显示当前行"
set magic "设置魔术?神马东东"
set ignorecase "搜索忽略大小写"
filetype on "打开文件类型检测功能"
set background=dark
set t_Co=256 "256色"
set mouse=a "允许鼠标"
" 配置nerdtree
let NERDTreeQuitOnOpen=1 "打开文件时关闭树
let NERDTreeShowBookmarks=1 "显示书签
" 配置nerdtree的快捷键
let mapleader = "," "设置前导键为,
map <leader>ne :NERDTreeToggle<CR> ",ne 打开 关闭 文件树
map <leader>tl :TlistToggle<cr> " ,tl 新建标签
nnoremap <leader>ma :set mouse=a<cr>
nnoremap <leader>mu :set mouse=<cr>
" tagbar插件
nmap <leader>tb :TagbarToggle<CR>

此时进入VIM中:

1
:PluginInstall

会自动帮你将配置文件中所需要的插件。

每个插件都需要下载安装,需要等待很长时间,建议吃饭的时候去做这件事。

最后一个安装的插件是'Valloric/YouCompleteMe',是必然会安装出错的插件,但这个插件是写C时的VIM神器,会提示很多信息,自动补全等,我们又不得不装,但是这个YCM,安装过程错误百出,十分麻烦,甚至有外国网友戏称之为FuckYouCompleteMe。。

那么下面是我遇到的所有YCM安装过程中的问题和解决办法总汇,如果你是在Ubuntu16.04,那么按照我的步骤,一定能让你顺利安装上这个恼人的YCM。

安装YCM过程中各种解决办法:

问题一:

1
YouCompleteMe unavailable: requires Vim 7.4.1578+.

vim版本不对,但实际上vim版本为要求的版本以上;

解决办法:

尝试升级vim为最新的vim8

1
2
3
sudo add-apt-repository ppa:jonathonf/vim
sudo apt update
sudo apt install vim

问题二:

1
Cannot add PPA. Please check that the PPA name or format is correct

解决办法:

1
2
3
sudo apt-get install --reinstall ca-certificates
sudo -E add-apt-repository ppa:ppaname/ppa

第一个通过ca证书方式,第二个是忽略代理添加ppa,如果还是Cannot add PPA,建议将系统升级到Ubuntu16.04或以上。在低版本的系统中,莫名其妙的PPA就是添加不上,update也是错误百出,将系统升级到了16.04,就顺利多了。

问题三:

1
YouCompleteMe unavailable: No module named ycmd

解决办法:

进入 ~/.vim/bundle/YouCompleteMe/文件夹下

1
2
3
$ git submodule update –init –recursive
$ ./install.py

问题四:

从这里开始的步骤和方法是必须要做的,如果你运气好,没有遇到前面的问题,那么下面的基本你几乎必须要做,才能成功安装上YCM

错误显示:

1
The ycmd server SHUT DOWN (restart with ':YcmRestartServer'). YCM core library not detected; you need to compile YCM before using it. Follow the instructions in the documentation

解决办法:进入 ~/.vim/bundle/YouCompleteMe/文件夹下

1
2
3
$ sudo apt-get install clang
$ sudo apt-get install python-dev python3-dev
$ ./install.sh --clang-completer --system-libclang

问题五:

1
NoExtraConfDetected: No .ycm_extra_conf.py file detected, so no compile flags are available.

缺少配置文件,所以我们添加一个就可以了

  • 复制 .ycm_extra_conf.py 文件
1
$ cp ~/.vim/bundle/YouCompleteMe/third_party/ycmd/examples/.ycm_extra_conf.py ~/.vim/
  • 添加 vim 配置
    注意下面的 python 解释器的路径要和编译 ycm_core 的时候使用的 python 解释器是相同的版本(2 或 3)
1
2
3
“ ~/.vimrc
let g:ycm_server_python_interpreter='/usr/bin/python'
let g:ycm_global_ycm_extra_conf='~/.vim/.ycm_extra_conf.py'

安装TagBar

使用一般IDE都会在侧面生成一个当前文件的结构图,就不说sublime里面还有个文件缩略图,那么在vim里我们也能添加这么一个tagbar,让我们在处理一个文件时,快速定位到函数变量,对代码了如指掌。之前已经成功装上了tagbar,但是使用TagBar之前先确保已经有ctags。

所以我们还要装一下这个ctags

1
sudo apt-get install ctags

使用

最后打开vim就可以使用打造好的IDE了,编辑C/C++就可以享受到代码提示,高亮,符号补齐等效果了,

按,ne 打开 关闭 文件树,,tb 打开 关闭 tablist等。

vps搭小飞机出海

发表于 2018-03-24 | | 阅读次数

梯子一旦用上了,就难以割舍了,就像中国第一封发现世界的email内容说的一样:越过长城,走向世界。

所以,这是非常有意义的事情,有意义的事情就得好好做。——许三多

买的一年的蓝灯用了不到两个月就各种问题,苟延残喘,估计挂了也是时间问题,之前一直买不到便宜的VPS,搬瓦工的便宜主机都被抢空了,赖哭。机缘巧合,接手了一个便宜VPS主机,十几块钱一个季度,主机是美国的,延迟和稳定性也就那样,不过这个价格十分符合我的要求,也就果断接手了。。。咳咳,等我有钱了,一定用最贵的。。。

有了主机,那么就是搭建啦~

一. 购买VPS

获得用户名密码,使用Xshell 5远程登录到主机上

二. 主机配置

1.修改下载源:

1
sudo vim /etc/apt/sources.list #修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
阿里源:
deb-src http://archive.ubuntu.com/ubuntu xenial main restricted #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial universe
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates universe
deb http://mirrors.aliyun.com/ubuntu/ xenial multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse #Added by software-properties
deb http://archive.canonical.com/ubuntu xenial partner
deb-src http://archive.canonical.com/ubuntu xenial partner
deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial-security universe
deb http://mirrors.aliyun.com/ubuntu/ xenial-security multiverse

更新设置

sudo apt-get update #更新列表

2.有没有python,没有安装python:apt-get install python

阅读全文 »

补码小记

发表于 2018-03-15 | | 阅读次数

前段时间看了斯坦福大学的编程范式公开课,第一课讲到补码的时候印象非常深刻,我发现名校名师都有一个特点,就是把讲课当做一回事,当做一个真正的事业来做,你可以从他讲课的流程方式就可以看得出他对这堂课用了多少工夫,而且不是企图一劳永逸的,而是时刻准备的去更新新的知识内容。

联想到在我当优课联盟当助教的这段时间里,也是颇为感触,同形形色色的老师交流过,也看过各式各样的在线课程。发现真正去传道授业解惑的老师,他一定是投入大量时间和精力在自己的课程内容上,和自身的成长上,并且他们都会做出很好的成绩并深受学生爱戴和欢迎,对于助教工作来说,能和这样的老师认识交流也是很愉快很成长的过程。

在现在大学老师都在纷纷做研究发paper,评职称,做生意,谋利益的时候,能遇到一个花大量时间打造自己课程为学生学习助力的老师的时候,真的是肃然起敬,作为学生,也忍不住要好好学习起来了。

围绕这个课程我有时间将笔记慢慢整理起来写成一系列的笔记吧,最近发现在本子上去写课程笔记没有用电脑敲字写的快了,看来真的是熟能生巧了。

二进制数补码小记

计算机中的整数是用什么码存储的?

答案是无符号类型是用原码存储,

有符号类型的正数用原码存储,

有符号类型的负数是用有符号正数的补码表示并存储的。

这个我以答案写在知乎上,以下:

补码是用来表示有符号的负数,不是signed numbers都用补码表示,signed numbers中正数的依然是以原码形式保存,

对有符号数取补码一方面好处是方便计算机进行有符号运算,

另一方面好处就是求有符号正数的补码时,可以自动将符号位处理一并处理了。

例如7的

原码:0 000 0111 (此时符号位为0,表示正数)

补码:1 111 1001 (前面的符号位自动在取补码的时候转换了)

计算机用此时7的补码来表示-7,这样有符号的负数运算就可以用电路实现:

0000 0111

+ 1111 1001

= 0000 0000

但是,这里需要注意一下,一个有符号负数求补码的话,此时不能顺便将符号位一并处理了,

需要单独将符号位孤立起来,剩下的部分再取反加一求补码,例:

-7二进制有符号数:

1 0000111

其补码:

1 1111001

所以,有符号二进制数在计算机里都是以补码的形式存在的,

即,有符号的-7是1000 0111,但在计算机里是以1111 1001形式储存的。

攻破网页字符替换反爬虫机制小记

发表于 2018-03-13 | | 阅读次数

本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循署名-非商业用途-保持一致的创作共用协议.

摘要:python3 进行网页爬取数据出现乱码,出现奇怪的编码方式,关键数据无法爬取,网站特殊字体的反爬机制的完整攻克过程。

最近在爬 实习僧 网站职位数据的时候遇到关键字体无法解析的问题。一开始还以为是chrome的解码格式不符合,解码错误导致的,但随着深入,发现这是可不是简单的编码问题,是网站精心策划的反爬机制。

第一次遇到这种情况,攻克的过程也挺复杂有趣,中间学到了很多东西,新手一开始遇到还真的会和我一样摸不着头脑,即使google可能也会从编码去查,绕很多弯路也百思不得其解。后来用这种发现这种方式的网站还挺多的,毕竟这种反爬机制是很易用的,想绕过去也是很麻烦的,所以写这篇文章,记录下整个攻克的方法和过程分享出来。

阅读全文 »

C++QT设计模式(第二版)

发表于 2018-03-05 | | 阅读次数

《C++QT设计模式(第二版)》读书笔记

Alan Ezust

Paul Ezust

好习惯:

定义类,应将它的定义放到一个与这个类同名的头文件中,并添加预处理宏来避免多次包含。例:aclass.h

1
2
3
4
5
6
7
8
9
10
11
#ifndef _ACLASS_H_
#define _ACLASS_H_
#include<Qstring>
class Aclass{
public:
...
};
#endif

再将这个类成员函数的具体实现写入一个单独的实现文件中去,命名为同名的cpp文件:

1
2
3
4
5
6
#include<QString>
#include"aclass.h"
QString Aclass::aprintfunc() const{
...;
}

默认情况下,类的成员是private类型

类的友元

友元可以是一个类,另外一个类的成员函数,或者任何一个非成员函数。

破坏封装性可能会危害程序的课维护性,因此应该尽量少的使用友元,不得已使用时候也要倍加小心。

通常而言,为了达到以下两个目的才是用友元函数:

  • 为了使用工厂方法,此时需要对某个类强制实施某些创建规则。(具体待补充)
  • 为了使用全局运算符函数,比如operator<<()

析构函数

如果某个类满足以下条件,则无需提供析构函数:

  • 只拥有非指针的简单类型成员
  • 拥有已经适当定义的析构函数的类成员
  • 它是满足某些条件的一种QT类(待补充)

stactic

全局命名空间污染:将名称添加到全局作用域中(例如,声明全局变量或者全局函数)

这是不好的编程风格

相对于全局变量,我们更倾向于使用static成员(且一般可以代替全局变量),因为static成员不会在全局命名空间中增加不必要的名称

每个static数据成员都必须在类定义之外的文件中被初始化(定义),一般在对应的类的实现文件中初始化。

.cpp中

int Thing::s_count = 0;

类的声明和定义

例:

egg.h:

1
2
3
4
5
6
7
[...]
#include"chinken.h"
class Egg{
public:
Chicken* getParent();
};
[...]

chicken.h:

1
2
3
4
5
#include "egg.h"
class Chicken{
public:
Egg* layEgg();
};

一个头文件包含另外一个头文件,另一个又包含回来了,预处理器不允许这样的循环依赖。

这两个头文件都不需要包含另一个头文件,因为这样做会不必要的在头文件之间建立一种强依赖性。

在正确的情况下,C++允许使用前置类声明,而不必包含某个特定的头文件。

例:egg.h

1
2
3
4
class Chinken;//前置类声明
class Egg{
Chinken* getparent();
};

注意:cpp文件中可以包含多个头文件,而不会导致它们之间的循环依赖性。.cpp文件对两个头文件具有依赖性,但头文件之间彼此不存在依赖性。

例:egg.cpp

1
2
3
4
5
#include"chicken.h"
#include"egg.h"
Chicken* Egg::getParent(){
return new Chicken();
}

因此,前置类声明使得定义双向关系成为可能,

转换构造函数

当一个构造函数只有一个参数,而且该参数又不是本类的const引用时,这种构造函数称为转换构造函数。

用转换构造函数可以将一个指定类型的数据转换为类的对象,但是不能反过来将一个类的对象转换为一个其他类型的数据。 类型转换函数的作用是将一个类的对象转换成另一个类型的数据。

转换构造函数的作用是将某种类型的数据转换为类的对象,当一个构造函数只有一个参数,而且该参数又不是本类的const引用时,这种构造函数称为转换构造函数。试验了一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class A
{
public:
int a;
A(int a) :a(a) {}
A reta()
{
return a;
}
};
int main()
{
A a(2);
A b = a.reta();
A c = 3;
cout<<b.a<<"\n"<<c.a<<endl;
return 0;
}

结果是输出2和3

实际上这是由隐式转换机制造成的,如果不想要这种效果,可以在构造函数前加上explicit声明。加上之后上面的代码就会编译出错,提示

无法从“int”转换为“A”。

既然能将数据转换为类型,类型也能转换为数据。c++的类型转换函数可以将一个类的对象转换为一个指定类型的数据。

类型转换函数的一般形式为 :

operator 类型名()

{实现转换的语句}

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class A
{
public:
int a;
A(int a) :a(a) {}
operator int()
{
return a;
}
};
int main()
{
A a(2);
int b = a + 3;
A c = a + 4;
cout<<b<<"\n"<<c.a<<endl;
return 0;
}

结果输出5和6

const成员函数

当将关键字const应用到一个(非static的)类成员函数时,其含义是比较特殊的。如果放置在参数之后,const就成为函数签名的一部分,从而保证此函数不会改变主题对象的状态。

要理解只一点,好方法就是意识到每一个非static成员函数都有一个称为this的隐式参数,this是一个指向主题对象的指针。当将一个成员函数声明为const时,就相当于告诉编译器只要此函数被调用,this就是一个指向const的指针。

可以看一下原始的C++到C语言的预处理器是如何处理成员函数的。因为C语言不支持重载函数或成员函数,预处理器会将这些函数翻译成具有“重整名称”的C语言函数,例:

1
2
3
4
5
6
cpp中
class Point{
public:
void set(int nx ,int ny){}
Qstring toString() const{}
};
1
2
3
c语言预处理后大概版本:
_Point_set_int_int(Point* this,int nx,int ny)
_Point_toString_string_const(const Point* this)

用QT实现Unicode显示

QtextStream能够针对Unicode的QString和其它类型的Qt起作用

所以配合QString的QtextStream可以顺利实现打印中文字符

1
2
3
4
5
6
7
8
9
10
11
#include<QTextStream>
#include<QString>
int main()
{
const char* charstr="这是中文";
QTextStream cout(stdout,QIODevice::WriteOnly);//WriteOnly,可省略:QTextStream cout(stdout);
QString str=charstr;
cout<<str<<endl;
return 0;
}

C++中避免使用数组

在C++中,数组被看成是“邪恶的”,

下面是应该避免在C++中使用数组的一些理由:

  • 编译器和运行时系统都不会检查数组的下标是否位于正确范围之内
  • 使用数组的程序员有责任编写额外的范围检查代码
  • 数组的大小可以是固定不变的,或者必须使用堆中的动态内存
  • 对于堆数组,程序员有责任确保所有情况下正确释放内存
  • 为此需要深入理解C++已经异常,特别是发生异常时的底层处理机制。向数组插入、预分配或追加元素都是费时的操作(再运行时和开发时都是如此)。

QStringList与迭代

例子:

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
#include<QStringList>
#include<QDebug>
int main()
{
QString winter="December,January,February";
QString spring="March,April,May";
QString summer="June,July,Auguest";
QString fall="September,October,November";
QStringList list;
list<<winter;
list+=spring;
list.append(summer);
list<<fall;
qDebug()<<"The spring mouths are: "<<list[1];
QString allmonths=list.join(", ");//把元素连成一个字符串
qDebug()<<allmonths;
QStringList list2=allmonths.split(", ");//按某个字符,把一个字符串拆成列表
qDebug()<<list2.size();
qDebug()<<list2[1];
foreach(const QString &str,list){//QT foreach循环——类似于python的for循环
qDebug()<<QString(" [%1] ").arg(str);
}
//C++STL风格的迭代
for(QStringList::iterator it =list.begin();it!=list.end();++it){
QString current =*it;
qDebug()<<"[["<<current<<"]]";
}
return 0;
}

运算符重载

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Complex{
friend ostream& operator<<(ostream& out,const Complex& c);
friend Complex operator-(const Complex& c1,const Complex & c2);
friend Complex operator*(const Complex& c1,const Complex & c2);
friend Complex operator/(const Complex& c1,const Complex & c2);
public:
Complex& operator+=(const Complex & c);
Complex& operator+=(const Complex & c);
Complex operator+(const Complex & c);//这里应该将运算符重载函数声明成非成员友元函数
private:
double m_r,m_i
}

例子中声明的运算符都是二元的(接收两个操作数)。对成员函数而言,却只有一个形式参数,因为第一个(左边的)操作数都是隐式的:*this

虽然可以将运算符重载为成员函数或者全局函数,但需要注意,它们的主要差异是调用它们的方式。特别地,成员函数运算符要求有一个用作左操作数的对象,而全局函数允许对任何一个操作数进行某种类型转换。

例:

1
2
3
4
5
6
7
8
9
10
//为什么+更适合作为非成员函数的理由
int main(){
Complex c1(4.5,1.2);
Complex c2(3.6,1.7);
Complex c3=c1+c2;
Complex c4=c3+1.4;//提升右操作数
Complex c5=8.0-c4;//提升左操作数
Complex c6=1.2+c4;//错误,左操作数没有针对成员运算符而提升
}

所以重载操作符应该视情况重载全局的运算符,将全局运算符重载函数声明成非成员友元函数。

从函数返回应用

有时候将函数设计成返回引用是非常有用的。例如,可以将多个操作“链”起来:

cout<<thing1<<thing2<<thing3...;

返回引用(尤其是*this)通常用来给成员函数提供左值行为。

采用引用参数,就可以通过将对象的别名指定成const来保护返回的引用。

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<iostream>
using namespace std;
int& maxi(int& x,int& y){
return(x>y)?x:y;
}
int main(){
int a= 10,b=20;
maxi(a,b)=5;//给b赋值5
maxi(a,b)+=6;//给a增加6,a现在为16
++maxi(a,b);//给a增加1
cout<<a<<"\t"<<b<<endl;
return 0;
}

注意:要小心,不要让函数返回一个临时(局部)对象的引用。当函数返回时,所有局部变量都销毁了。

inline函数的一些规则

  • inline函数必须在被调用之前定义(仅仅声明它是不够的)。
  • 在一个源代码模块中只能有一次inline定义。
  • 如果类成员函数的定义出现在类定义之内,则成员函数就是隐含inline的。

如果函数太复杂,或者编译器选项改变了,则编译器可能会忽略inline指令。大多数编译器会拒绝包含如下语句的inline函数:

  • while,for,do-while语句
  • switch语句
  • 超过一定数量的代码行

继承和多态

没有virtual时候*p1和*p2的具体类型靠函数指针类型来判断:

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<iostream>
class A
{
public:
void getnum() {//A类中没有virtual函数
std::cout << a << std::endl;
}
private:
int a;
};
class B:public A
{
public:
void getnum() {
std::cout << b<< std::endl;
}
private:
int b;
};
int main() {
A* p1 = new(A);
A* p2(new B);
std::cout << typeid(p1).name() << std::endl;//打印A*,指针类型始终不变
std::cout << typeid(p2).name() << std::endl;//打印A*,指针类型始终不变
std::cout << typeid(*p1).name() << std::endl;//打印A
std::cout << typeid(*p2).name() << std::endl;//打印A
std::cin.get();
return 0;
}

有virtual时候*p1和*p2的具体类型通过指向类型判断,虚函数有虚函数表,可以确定数据类型:

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<iostream>
class A
{
public:
virtual void getnum() {
std::cout << a << std::endl;
}
private:
int a;
};
class B:public A
{
public:
void getnum() {
std::cout << b<< std::endl;
}
private:
int b;
};
int main() {
A* p1 = new(A);
A* p2(new B);
std::cout << typeid(p1).name() << std::endl;//打印A*,指针类型始终不变
std::cout << typeid(p2).name() << std::endl;//打印A*,指针类型始终不变
std::cout << typeid(*p1).name() << std::endl;//打印A
std::cout << typeid(*p2).name() << std::endl;//打印B,根据虚函数表判断具体指向数据类型
std::cin.get();
return 0;
}

优雅的在latex中添加参考文献

发表于 2018-01-22 | | 阅读次数

BibTeX 是一个使用数据库的的方式来管理参考文献程序, 用于协调LaTeX的参考文献处理.

BibTeX 文件的后缀名为 .bib . 先来看一个例子:

@article{Gettys90,
author = {Jim Gettys and Phil Karlton and Scott McGregor},
title = {The {X} Window System, Version 11},
journal = {Software Practice and Experience},
volume = {20},
number = {S2},
year = {1990},
abstract = {A technical overview of the X11 functionality. This is an update of the X10 TOG paper by Scheifler \& Gettys.}
}

在文献网站上都有导出bib数据库格式的链接,

把所有的引用卸载一个.bib文件中,例如test.bib,

然后在文章中导入:\bibliography{test}

就完成了文章引用了~

c++中异常的简单处理

发表于 2018-01-15 | | 阅读次数
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
#include <iostream>
#include <string.h>
using namespace std;
//标识错误的类型
void wrong ()
{
};
int intdiv(int a, int b)
{
try
{
if (b==0)
{
throw 10;//可以是任何对象 ,也可以throw一个函数wrong();
}
int c = a / b;
return c;
}
catch (int data )//类型名,catch的参数捕获的是throw的东西
{
cout << "除法异常已经处理";
return -1;
}
}
int intdivA(int a, int b)
{
return a / b;
}
void main()
{
int x, y;
cin >> x >> y;
try
{
if (y==0)
{
throw "被除数为0";
}
else if (x==0)
{
throw "除数为0";
}
}
catch (const char * s)//catch 捕获的是throw的东西,throw字符串的时候,捕获也要捕获字符串进行相应处理
{
if (strcmp(s,"被除数为0")==0)
{
cout << "被除数为0异常,请重新输入";
cin >> x >> y;
}
else if (strcmp(s, "除数为0") == 0)
{
cout << "除数为0异常,请重新输入";
cin >> x >> y;
}
}
std::cout << intdiv(x, y);
cin.get();
cin.get();
cin.get();
}

C++文件流读写

发表于 2018-01-01 | | 阅读次数

流被用来读取/写入文件、连接网络和字符串,它的一个有用的特性是易于从混合数据类型中得到字符串。

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
#include<iostream>
#include<sstream>
#include<fstream>
int main() {
using namespace std;
ostringstream strbuf; //字符流声明
int lucky = 7;
float pi = 3.14;
double e = 2.71;
cout << "an in-memory stream" << endl;
strbuf << "lucknumber" << lucky << endl//将各种类型的值和字符串一起传给字符流,然后再做统一处理
<< "pi:" << pi << endl
<< "e:" << e << endl;
string strval = strbuf.str();//将字符串流转换成字符串
cout << strval;//打印字符串
ofstream outf;//声明openfile对象
outf.open("mydata");//打开文件,没有则创建
outf << strval;//将字符串流输出给文件
outf.close();//关闭文件
cout << "read data from the file" << endl;//读文件
string newstr;
ifstream inf;//声明一个输入文件流对象
inf.open("mydata");//打开文件
if (inf) {//Make sure the filr exists before attempting to read
while (! inf.eof()) {//文件流每次读取为一行,读取后会自动进入下一行,直到inf.eof()
getline(inf, newstr);//把读取的每行当做字符串处理
cout << newstr << endl;
}
inf.close();
}
cin.get();
return 0;
}

QT中实现

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
#include<QTextStream>
#include<QString>
#include<QFile>
QTextStream cout(stdout);
QTextStream cerr(stderr);
int main()
{
QString str,newstr;//Qstring为QT中的string,有丰富的API更好用
QTextStream strbuf(&str);//QT的字符流声明
int lucky =7;
float pi =3.14;
double e=2.71;
cout<<"A in-memory stream"<<endl;
strbuf<<"luckynumber:"<<lucky<<endl//一样先把字符都输入到字符流中,方便后面统一处理
<<"pi:"<<pi<<endl
<<"e:"<<e<<endl;
cout<<str;
QFile data("mydata");//创建QT文件类对象,并用文件初始化QFile对象
data.open(QIODevice::WriteOnly);//打开文件,以QTIO的写方式
QTextStream out(&data);//这里和VC不太一样,要输出到文件,先要将文件转换成对应的字符串流
out<<str;//输出到已绑定字符流文件
data.close();
cout<<"Read from file line-by-line"<<endl;
if(data.open(QIODevice::ReadOnly)){
QTextStream in(&data);
while (not in.atEnd()){
newstr = in.readLine();
cout<<newstr<<endl;
}
data.close();
}
return 0;
}

小试QT GUI

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<QtGui>
#include<QMessageBox>
#include<QInputDialog>
#include <QApplication>
int main(int argc,char* argv[]){
QApplication app(argc,argv);
QTextStream cout(stdout);
int answer = 0;
do{
int factArg=0;
int fact(1);
factArg = QInputDialog::getInt(0,"Factorial Calculator","Factorial of:",1);//QT的输入对话框
cout<<"User entered:"<<factArg<<endl;//打印到控制台
//业务内容
int i =2;
while(i<=factArg){
fact=fact*i;
++i;
}
QString response=QString("The factorial of %1 is %2.\n%3")
.arg(factArg).arg(fact)
.arg("Do you want to computer another factorial?");//Qstring格式化字符串内容
answer=QMessageBox::question(0,"play again?",response,
QMessageBox::Yes|QMessageBox::No);//对话框设置
}while(answer==QMessageBox::Yes);
return EXIT_SUCCESS;
}

QString

可以将char类型的字符串用等号之间转换过来

1
2
3
4
5
6
7
8
9
10
11
#include<QTextStream>
#include<QString>
int main()
{
const char* charstr="this is a char string";
QTextStream cout(stdout);
QString str=charstr;
cout<<str<<endl;
return 0;
}

c++lambda表达式

发表于 2017-12-27 | | 阅读次数

本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循署名-非商业用途-保持一致的创作共用协议.

C++ 11中的Lambda表达式用于定义并创建匿名的函数对象,以简化编程工作。Lambda的语法形式如下:
[函数对象参数](操作符重载函数参数) mutable或exception声明->返回值类型{ 函数体 }

可以看到,Lambda主要分为五个部分:

[函数对象参数]、(操作符重载函数参数)、mutable或exception声明、->返回值类型、{ 函数体 }。

下面分别进行介绍。
一、[函数对象参数],标识一个Lambda的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。
函数对象参数只能使用那些到定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的this)。函数对象参数有以下形式:

  • 空。没有使用任何函数对象参数。

  • = 。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。

  • &。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。

  • this。函数体内可以使用Lambda所在类中的成员变量。

  • a。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。

  • &a。将a按引用进行传递。

  • a, &b。将a按值进行传递,b按引用进行传递。

  • = ,&a, &b。除a和b按引用进行传递外,其他参数都按值进行传递。

  • &, a, b。除a和b按值进行传递外,其他参数都按引用进行传递。

二、(操作符重载函数参数),标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a, b))和按引用(如:(&a, &b))两种方式进行传递。

三、mutable或exception声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。
exception声明用于指定函数抛出的异常,如抛出整数类型的异常,可以使用throw(int)。
四、->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。
五、{ 函数体 },标识函数的实现,这部分不能省略,但函数体可以为空。

几个例子:

阅读全文 »
1…789…11
Yang Shuai

Yang Shuai

105 日志
89 标签
© 2021 Yang Shuai
由 Hexo 强力驱动
主题 - NexT.Mist