My FAQ about c++/c/mfc programming
1.
如何通过iterator
取得结构中的值?
比如,
map<pair<int_left,int_right>,int_value>::iterator iter;
通过
iter
访问它所指对象的值,做法如下:
int left_int = (*iter).first.first;
int right_int = (*iter).first.right;
int value_int = (*iter).second;
2.
如何通过iterator
取得下标或者相反。
例如,
vector<int>::iterator iter;
int index = iter-vec.begin();
iter = vec.begin()+index;
3.
如何取得随机整数?
Example:
#include<time.h>
#include <stdlib.h>
main()
{
int BIG_NUM = 100;
srand( (unsigned)time( NULL ) );
while(true){
cout << rand_num = rand() %100;//输出0~99的整数
}
}
4.
如何给vector
预设大小?
如果事先知道
vector
所需空间大小,不需要动态增长,以便节约空间。在定义
vector
后可以调用
reseave
函数预设其大小。
Vector<int> vec;
Vec.researve(100);//
预设
100
5.
泛型容器和泛型算法中的predicate function object(
谓词函数对象)
如何写?
比如在调用泛型容器
map
或者泛型算法如
:sort,lower_bound,upper_bounder,equal_rang
的时候都需要指明参数:
“BinaryPredicate _Comp”
。
如何用呢?通过一个使用
sort
的例子来说明一下。
假定,有一个结构
node
里面包含一个整形值,一个
string
型值。现在想对一个
vector<node>
型对象
vec_node
里面的内容用
sort
进行排序。那么有三种方法。
方法
1:
不给
sort
显式提供比较准则,那么会自动调用
vec_node
里面所放类型的
”<”
操作,如果没有重载
”<”
,则编译器报错。
方法
2:
显式提供比较准则
,
用一般的函数来提供。
方法
3:
显式提供比较准则
,
用
function object
来提供。
方法
2,3
其实一样,
不过用
function object
比一般的函数指针有很多的优点。
Function objects (also called "functors") are ordinary class objects that overload the () operator. Thus, syntactically, they behave like ordinary functions.
对于
map
容器,因为它本身是有序容器,所以存放用户定义的较复杂的结构时也需要自己定义
predicate
,不过是对于
key
类型进行比较的。
但是一般的如
key
为
int,
或者
key
类型为
pair<int,int>
的情况下,
map
结构有默认的增序排列。
示例代码如下:
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
typedef struct node{
int value;
string key;
public :
bool operator < (const node n) //方法1:重载”<”操作符
{
return value > n.value;
}
}node;
class func2{//方法2:定义function object
public:
bool operator() (const node n1,const node n2)
{
return n1.value > n2.value;
}
};
bool func3(node n1,node n2) //方法3:定义一般函数
{
return n1.value > n2.value;
}
void main()
{
vector<node> intVec;
node n1 ;
n1.key = "hf4";
n1.value = 4;
intVec.push_back(n1);
n1.key = "hf2";
n1.value = 2;
intVec.push_back(n1);
n1.key = "hf1";
n1.value = 1;
intVec.push_back(n1);
n1.key = "hf3";
n1.value = 3;
intVec.push_back(n1);
n1.key = "hf5";
n1.value = 5;
intVec.push_back(n1);
sort(intVec.begin(),intVec.end());//方法1:重载”<”操作符
sort(intVec.begin(),intVec.end(),func2());//方法2:定义function object,注意加’()’
sort(intVec.begin(),intVec.end(),func3);//方法3:定义一般函数
for(int i=0;i<intVec.size();i++)
{
cout << intVec[i].value << "\t";
}
}
6.
泛型算法里面的二分查找算法都有哪些?如何使用?
有比如
lower_bound,upper_bound,equal_range
这类的。
一个找给定区间的下限,一个上限,一个是区间。
使用时一样要根据情况看需不需要写
predicate,
但是有些细微问题还需要注意,以
lower_bound
的用法为例。
typedef struct BigramNode{
int LeftWrdID;
int RightWrdID;
double LogProb;
}BigramNode;
class Comp
{
public :
bool operator()(const BigramNode n1,const int WordID)
{
return n1.RightWrdID<WordID;
}
//注意点1:两个方向的比较都要考虑到。
bool operator()(const int WordID,const BigramNode n2)
{
return n2.RightWrdID<WordID;
}
};
vector<BigramNode>::iterator BiNodeIter;
vector<BigramNode>::iterator StartIter = m_vBigramNodes.begin() + BigramsStartP;
vector<BigramNode>::iterator EndIter = m_vBigramNodes.begin() + BigramsEndP;
//
注意点
2
:如果索要找的范围内的值是不重复的,那么先要看尾部迭代器所指是否为
//
所求。
if((*EndIter).RightWrdID == RightWordID)
{
return (*EndIter).LogProb;
}
BiNodeIter = lower_bound(StartIter,EndIter,RightWordID,Comp());
//注意点3:找完后要查看两点,结果是所求而且不是给定查找范围的尾部。因为//如果找不到就会返回查找区间尾部。
if(BiNodeIter != EndIter && (*BiNodeIter).RightWrdID == RightWordID)
{
return (*BiNodeIter).LogProb;
}
7.
1.
文本的读写常用的操作?
在这里先总结一下
c/c++/MFC
中文本处理常用的文件读写。
1.C
里面
a) 在标准c里面,所有的文件读写操作都要通过文件指针进行,即FILE * fp;
b) 对于文件模式是二进制还是文本模式,每种处理方法都是一样的,就比如采用fscanf/fprintf 打开读写二进制文本还是文本模式文本,其效果是一样的。(个人意见,简单试验后的看法)
c) 用 fscanf/fprintf函数对磁盘文件读写,使用方便,容易理解,但由于输入时要将ASCII字符转换成二进制数,输出时要将二进制数转换成ASCII字符,花费时间较多,因此,在内存与磁盘频繁交换数据的情况下,最好不用fprintf和fscanf函数,而用fread和fwrite函数。
d) FILE
文件的打开和关闭
i.
打开:
fp = fopen(filename,
文件使用方式
)
;如果打开不成功,返回一个空指针
NULL.
ii.
关闭:
fclose(fp);
要养成关闭文件的习惯,因为
c
编译系统允许打开的文件数是有限的,而且可以防止丢失缓存区中的数据。
iii.
判断文件是否结束:
1. while(! feof(fp)) {
如果文件未结束进行操作
}
e) 文件的读写方式有三种,
i.
按字符
(
串
)
读写
1. char ch = fgetc(fp); fputc(ch,fp);
2. char *fgets( char *string, int n, FILE *stream );
3. int fputs( const char *string, FILE *stream );
4. 想读入一行的话,可用fgets(str,ENOUGH_BIG_NUM,fp); 因为fgets碰到newline 字符(‘\n’)会自动截断。
ii.
按数据块读写文件
1. fread(buffer,size,count,fp);
2. fwrite(buffer,size,count,fp);
3. 返回值:读/写的数据块数
4. 举例:struct MT *ebmt, *smt;
a) FILE * fp;
If((fp=fopen(“mt_method.dat”,”wb+”))==NULL){
Printf(“open error”);
Return;
}
ebmt = new MT;
smt = new MT;
…..//
赋值
//
写入文件
fwrite(ebmt,sizeof(struct MT),1,fp);
fwrite(smt,sizeof(struct MT),1,fp);
struct MT *new_ebmt, *new_smt;
//read in
fread(new_ebmt,sizeof(struct MT),1,fp);
fread(new_smt,sizeof(struct MT),1,fp);
fclose(fp);
iii.
按格式读写
1. fscanf(
文件指针,格式控制参数,地址参数列表
)
;
2. fprintf(
文件指针,格式控制参数,地址参数列表
)
;
3. FILE *stream; char str[];
◎
如果想每次输入一个词:
fscanf(fp,”%s”,str);
因为
fscanf
可以按
White-space characters: blank (' '); tab ('\t'); or newline ('\n').
来截断。但是要注意在用
fscanf
取词的时候,特别要注意最后一个词是否会取到两次。比如在文本末尾是几行空行。
如果代码为
void main( void )
{
FILE *stream;
char line[100];
if( (stream = fopen( "fgets.c", "r" )) != NULL )
{
while(!feof(stream))
{ fscanf( stream, "%s", line );
printf( "%s#\n", line);
}
fclose( stream );
}
}
则最后一个词就会被输出两次,原因是,最后一次因为文件中还有回车换行符所以
while
循环进来,然后
fscanf
没取到新的词,
line
中保存的依然是上一个词,再次被输出,然后
while
循环也终止。所以要对
fscanf
的执行情况判一下,这里可以判断一下是否到了文件尾。改动上面加黑部分为:
if(fscanf( stream, "%s", line )!=EOF)
printf( "%s#\n", line);
2. 在c++中
a) C++
中为输入输出提供了丰富的流库,具体信息可以参看
<<C++ Primer 3rd Edition>>
中的第
20
章。在这里,只是简单地介绍一下常用的文件流操作。
b) 文件打开创建关闭:
i.
输入
/
出文件流的创建打开:
ifstream in(“input.txt”);
或者
ifstream in; in.open(“input.txt”);
或者
fstream outfile(“output.txt”,ios_base::out)
,追加模式的话,
fstream outfile(“output.txt”,ios_base::app);
判断是否正确打开:
if(!in) cerr<< “input file open error.\n”;
ii.
关闭:
filestream.close();
c) 读入一行或者一个词
ifstream infile(“infile.txt”);
string line,next_word;
getline(infile,line)//
第三个参数分隔符默认为
’\n’
//
如果词汇之间用
white characters(‘\t’,’ ’,’\n’)
分割
,
那么读入一个词
infile >> next_word;
d) 输出
i.
一般用
outfile<< out_content;
就够了。
3. 在VC/MFC中
a) 首先我们看到在MFC层次表中的关于文件的继承关系图如下:在我们文本处理实际操作中常用的两个文件类型是CFile和CStdioFile.
b) CStdioFile
继承自
CFile,
而且
CFile
directly provides unbuffered, binary disk input/output services, and it indirectly supports text files and memory files through its derived classes
Use CFile and its derived classes for general-purpose disk I/O. Use ofstream or other Microsoft iostream classes for formatted text sent to a disk file.
CFile
除了
Read/Write
外,还提供了很多函数比如
GetFilePath, GetFileName
等。
CStdioFile
A CStdioFile object represents a C run-time stream file as opened by the run-time function fopen. Stream files are buffered and can be opened in either text mode (the default) or binary mode.
Text mode provides special processing for carriage return–linefeed pairs. When you write a newline character (0x0A) to a text-mode CStdioFile object, the byte pair (0x0D, 0x0A) is sent to the file. When you read, the byte pair (0x0A, 0x0D) is translated to a single 0x0A byte.
CStdioFile
比
CFile
多提供了两个函数:
Text Read/Write
Reads a single line of text.
| |
Writes a single line of text.
|
8.
8. vector.reserve(N)和vector.resize(N)
前者是预留N个空间,并不填充,此时返回的vector.size()还是0,而且这样就要去访问它的某个元素,如vector[10]的话就会出现运行时错误。后者不仅预留了空间,而且为每个空间调用元素对应的构造函数填充了默认的值。实际上就是push_back了N个默认的值。另外记住,在刚定义了一个vector时,还没有用resize()这样的函数填充任何元素的时候,要往里面填元素,必须用push_back(),而不能通过vector[index] = item;这样的
操作。
9. 如何让程序得到运行时间
例子程序:
#ifdef WIN32
// The FILETIME structure is a 64-bit value representing the number of
// 100-nanoseconds. One nanosecond = 10e-9 seconds.
typedef __int64 INT64;
FILETIME ftDummy, ftKernel, ftUser;
GetProcessTimes(GetCurrentProcess(), &ftDummy, &ftDummy, &ftKernel, &ftUser);
printf("real %.3fs\n", double(clock() - startClk) / CLOCKS_PER_SEC);
printf("user %.3fs\n", (INT64(ftUser.dwHighDateTime << 32) | ftUser.dwLowDateTime) / 1e7);
printf("sys%.3fs\n", (INT64(ftKernel.dwHighDateTime << 32) | ftKernel.dwLowDateTime) / 1e7);
#endif
其中的clock() 是在”time.h”中的函数 ,startClk 是一个clock_t 类型。
GetProcessTimes是一个windows API函数,用来返回当前进行所用的相关时间。
10. TBC