好像有这么个说法:“代码都在网上,学会google你就学会了coding”,确实是如此,学会向搜索引擎提问,问得越到位,得到的答案越精准而且快速。这不仅限于coding,所有一切,只要有疑问的都可以向搜索引擎提问,或多或少都会有些帮助,前提是客观的搜索引擎。然而实际生活中,并不是人人都善于利用搜索引擎来解决问题,大多的时候都喜欢让人代工,我自己以前就常常这么干,明明自己上网搜一下就可以解决的问题,却总是问别人,想着谁能告诉你怎么做。这就是典型的不思考。虽然一般情况下强调学习要多提问,然而多提问很多时候并不一定就是好的,更要注重提问的质量。能自己解决的尽量自己解决,不要让自己成为一个一遇到问题就问人的问题机器,要多思考,注重独自解决问题的能力,求助别人的时候,要搞清楚自己要问的是什么,而不是稀里糊涂的把锅一甩,这不是提问,这是让别人给你做了。

对于coding这件事,不止于会问,如果想要有提高,夯实基础和总结积累是两个很重要的方面。夯实基础即是要多看书,看好书,看经典。把基本的原理,概念要理解透。总结积累即是要在实践的过程中,对每次遇到的问题、困难进行总结提炼,遇到的问题是什么,自己是怎么解决的,总结的一个好方式就是做笔记写备忘,所谓好记性不如烂笔头,与其相同的问题一次次重复地遇到不如把每次遇到的问题及解决方法都记录下来,一是加深了理解,二来把东西放在自家后院,那才是自己的,用起来或者平时翻出来看看也是方便。

刚刚开始写笔记的时候常常会觉得,这个东西太简单了,用一次就记住了,没有必要去写。其实并不是这样。简单又何妨,就我个人经验而言,只要是一开始把你难住的问题,往往还会有第二次,第三次。所以,多思考,勤笔记,下次再遇则有迹可循。只有足够努力,方显毫不费力。想做一件事,任何时候都不算太晚,除非只是想想而已。

呵呵。口号喊完了,该开始写笔记了。我将在这篇笔记中不断地记录自己平时在使用C++时遇到并解决的小问题,并不需要特意去花上半天一天来总结,记录这一动作就发生在平时遇到问题并解决后。


CString与string的区别及转化

关于CString类型:CString是MFC的类,利用MFC进行编程时会经常用到,而一些标准C/C++库函数是不能直接对CString类型进行操作的,所以经常遇到将CString类型转化char*等其他数据类型的情况。

关于string类型:标准C中是不存在string类型的,string是标准C++扩充字符串操作的一个类,但是标准C中有<string.h>这个头文件,在里面定义了一些经常用到的操作字符串的函数,如:strcpy、strcat、strcmp等,这些函数的操作对象都是char*指向的字符串。

而C++的string类(头文件是<string>)操作对象是string类型字符串,该类重载了一些运算符,添加了一些字符串操作成员函数,使得操作字符串更加方便。有时候需要将string串和char*串配合使用,因此也会涉及到这两个类型的转化问题。

上述是CSting、string.h和string的区别,要对CString和string类型的字符串进行转换,首先将类型转化为char*类型,因为char*是不同类型之间的桥梁。得到char*类型,转化为其他类型就非常容易了。

1、string to char

1
2
3
4
char ch[255];
string str = "string to char";
strcpy(ch, str.c_str());
cout << ch << endl;

2、string to char *

1
2
3
4
5
6
7
string str = "hello world";
char *strChar;
int len = str.length();
strChar = (char *)malloc((len+1)*sizeof(char));
str.copy(strChar,len,0);
printf("%s",strChar);
cout<<strChar;

3、char to string

1
2
3
char ch[255] = "ch to string";
string str = string(ch);
cout << str << endl;

4、string to CString(在Unicode和多字节字符集环境下)

1
2
3
4
string str = "string2CString";
CString cstr;
cstr = str.c_str();
MessageBox(cstr);

5、CString to string(在Unicode和多字节字符集环境下)

1
2
3
CString cstr = _T("Cstring2string");
string str;
str = (CStringA)cstr;

6、string to double

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// string to double
string = "3.14159";
double PI=atof(str.c_str());

// 将double值转换为string,string转double,或者其他类型
double length=5.38725;
string tempStr;
stringstream ssStr;
ssStr<<length;
ssStr>>tempStr;
CString msg;
msg=tempStr.c_str();
MessageBox(msg);
ssStr.clear();

7、将int转换为CString

1
2
3
4
int num=123;
CString intStr;
intStr.Format(_T("%d"),num);
MessageBox(intStr);

8、 CString转char*(解决在Unicode字符集环境下中文乱码问题)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Unicode环境下将CString转换成char*
char* CStringToChar(CString inputStr)
{
//获取宽字节字符的大小,大小是按字节计算的
DWORD dwLength=WideCharToMultiByte(CP_ACP,NULL,inputStr,-1,NULL,0,NULL,FALSE);

//为多字节字符数组申请空间,数组大小为按字节计算的宽字节字节大小
char *charStr;
charStr = new char[dwLength];
if(!charStr)
{
delete []charStr;
}
//宽字节编码转换成多字节编码
WideCharToMultiByte(CP_ACP,NULL,inputStr,-1,charStr,dwLength,NULL,FALSE);
return charStr;
}

9、MFC 多字节字符集环境下 CString转char*

1
2
CString Length="10";
char *LengthStr=Length.GetBuffer(0);

10、Unicode环境下将 CString转int

1
2
CString str=_T("123");
int i=_ttoi(str);

C中字符串操作

1、由于字符串是数组类型,所以两个字符串赋值运算不能直接用“=”(除了初始化时),字符串的比较也不能直接用”==”,,字符串的拼接也不能用”+”,在C中有专门的操作函数,如:strcpy、strcat、strcmp。

char strcpy(char strDest,const char *strSrc); //是字符串复制函数

1
2
3
char strDest[255] = "Hello";
char strSrc[255] = "World!";
strcpy(strDest,strSrc); // strDest=World!

char strcat(char strDest,const char *strSrc); //是字符串拼接函数

1
2
3
char strDest[255] = "Hello";
char strSrc[255] = " World!";
strcat(strDest,strSrc); // strDest=Hello World!

int strcmp(const char str1, const char str2 ; //是字符串比较函数

1
2
3
4
5
6
char str1[255] = "Hello";
char str2[255] = " World!";
if(strcat(str1,str2)!=0)
{
cout << "str1 not equal to str2!" << endl;
}

2、字符串查找

1
2
3
4
5
string setEndFindStr="END SETS";
if (lineStr.find(nodesFindStr) < lineStr.length())
{
cout << find "END SETS" << endl;
}

格式化字符串

1、用sscanf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
getline(_inputFile, firstLineStr);
char setType[255];
char setName[255];
sscanf(firstLineStr.c_str(), "%*[^,],%[^,],%[^,],%*[^,]", &(setName),&(setType));

//解释一下,在这里firstLineStr=CMBLOCK,ELESET1 ,ELEM,28 !users element component definition
//格式化字符串后,setName==ELESET1,setType==ELEM
//%[]类似于一个正则表达式。[a-z]表示读取a-z的所有字符,[^a-z]表示读取除a-z以外的所有字符。
//所以此处``%*[^,]``表示满足``[]``里的条件将被过滤掉,不会向目标参数中写入值。
//即:将在遇到第一个``,``之前的(不为``,``的)字符全部过滤掉。

//其他简单用法
//Splitting the string,e.g."*node(1,0.292676,0.078422,-0.07,0,0,0,5,86)"
sscanf(firstLineStr.c_str(), "*node(%d,%lf,%lf,%lf,%*s,%*s,%*s,%*s,%*s",
&(node.id), &(node.coordinate[0]), &(node.coordinate[1]), &(node.coordinate[2]));

2、用strtok
strtok的函数原型为char strtok(char s, char *delim),功能为“Parse S into tokens separated by characters in DELIM.If S is NULL, the saved pointer in SAVE_PTR is used as the next starting point. ” 翻译成中文就是:作用于字符串s,以包含在delim中的字符为分界符,将s切分成一个个子串;如果,s为空值NULL,则函数保存的指针SAVE_PTR在下一次调用中将作为起始位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//string lineStr="3	-49	17	-19	26	-29	33	-34",以“ ”作为分隔符
char lineData[255] = { 0 };
strcpy(lineData, lineStr.c_str());
const char *delimter = " ";
char *item;
vector<int> itemCounter;
item = strtok(lineData, delimter);
while (item)
{
//把char类型的转换成int类型存入临时变量
int nodeInt = 0;
string nodeStr(item);
stringstream nodeSs(nodeStr);
nodeSs >> nodeInt;
itemCounter.push_back(nodeInt);
item = strtok(NULL, delimter);
}

3、用AfxExtractSubString

1
2
3
4
5
6
7
8
9
10
//参考PDS系统代码,MatlabSample.cpp
//以tab键为分割符,将apdl几何变量存到容器
CString tempName;
for (int i=ugVariableNum; i<(ugVariableNum+apdlVariableNum); i++)
{
//tempName得到的字符串,tempStr完整待分割的字符串,i取第几个放在tempName
AfxExtractSubString(tempName,tempStr,i,'\t');
apdlVariableName.push_back(tempName);
/*MessageBox(tempName);*/
}

4、用Cstring中的Find()、Mid()、Replace()、Right()、Left()等用法

1
2
3
4
//参考机场安保系统代码,Logical_Relationship.cpp
int charPOS=indexstr_old.Find(' ');
Cstring temp=indexstr_old.Right(indexstr_old.GetLength()-charPOS-1);
index=atoi(temp)+1;

写文件的几种方式

1、用FILE

1
2
3
4
FILE *outputFile = NULL;
outputFile = fopen("test.txt", "w");
fprintf(outputFile, "%d\t%d\t%d\t%d\n", dimension, 3, nodes.size(), tria3.size());
fclose(outputFile);

2、用ofstream

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ofstream ofs("test.txt",ofstream::out);	

ofs << "# vtk DataFile Version 2.0" << endl
<< "VTK file" << endl
<< "ASCII" << endl
<< "DATASET UNSTRUCTURED_GRID" << endl
<< "POINTS " << nodes.size() << " float" << endl;

for (int i = 0; i < nodes.size(); i++)
{
ofs << nodes[i].coordinate[0] << " "
<< nodes[i].coordinate[1] << " "
<< nodes[i].coordinate[2] << endl;
}

3、用CStdioFile

1
2
3
4
5
6
7
8
9
10
CStdioFile outPutFile( _T("temp.txt"),CFile::modeCreate | CFile::modeWrite | CFile::typeText );

CString headStr=_T("!设置工作目录\n");
outPutFile.WriteString(headStr);

CString stressDataStr;
stressDataStr.Format(_T("*cfopen,DATA-stress-result,txt,'%s'"),G_projectPath);
outPutFile.WriteString(stressDataStr);

outPutFile.Close();

读文件的几种方式

1、用ifstream

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ifstream infile;
infile.open(filepath);
if (!infile.is_open())
{
return -1;
}

string line = "";
while (!infile.eof())
{
getline(infile, line);
cout << line << endl;
}
infile.close();

2、用CStdioFile

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
CStdioFile apdlFile;
CString apdlPath=_T("test.txt");
if (!apdlFile.Open(apdlPath, CFile::modeRead))
{
::AfxMessageBox(_T("文件打开失败。"));
return;
}

CString apdlStr = _T("");

//计算文件的行数
int lineCount=0;
while (apdlFile.ReadString(apdlStr))
{
lineCount++;
}
apdlFile.Close();

//将文件写入一个动态数组
CString *apdlArray=new CString[lineCount];
apdlFile.Open(apdlPath, CFile::modeRead)

int writeNumber=0;
while (apdlFile.ReadString(apdlStr))
{
apdlArray[writeNumber]=apdlStr;
writeNumber++;
}
apdlFile.Close();

const 与 #define的比较 @2016年4月5日10:09:41

C++ 语言可以用const来定义常量,也可以用 #define来定义常量。但是前者比后者有更多的优点:

  • const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应)。
  • 有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。

    「规则1」在C++ 程序中只使用const常量而不使用宏常量,即const常量完全取代宏常量。

  • 常量定义规则

    「规则2」需要对外公开的常量放在头文件中,不需要对外公开的常量放在定义文件的头部。为便于管理,可以把不同模块的常量集中存放在一个公共的头文件中。
    「规则3」如果某一常量与其它常量密切相关,应在定义中包含这种关系,而不应给出一些孤立的值。

例如:

1
2
3
#define MAX 100 // C语言的宏常量 
const int MAX = 100; // C++ 语言的const常量
const float PI = 3.14159; // C++ 语言的const常量


main函数中argc和argv参数的含义 @2016年4月10日 16:32:23

1
2
3
int main (int argc, char *argv[])
//argc=argument count
//argv=argument value

使用geline读文件时判断文件末尾的问题 @2016年4月17日00:35:15

使用getline(infile, lineStr);时,如果infile文件中最后一行不是空行,则会死循环(跳不出循环),这可能是LmeTestTool中有空行没空行时计算出bug的症结之所在。有时间可以去检查一下代码,在这做个备忘。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ifstream infile;
infile.open("RBF-Init-Data-Points.txt");
if (!infile.is_open())
{
return -1;
}

string lineStr = "";
getline(infile, lineStr);
while (lineStr != "")
{
int lifeTemp = 0;
sscanf(lineStr.c_str(), "%d", &(lifeTemp));
cout << lifeTemp << endl;
lifeVector.push_back(lifeTemp);
getline(infile, lineStr);
}
infile.close();

检测EOF:成员函数eof()用来检测是否到达文件尾,如果到达文件尾返回非0值,否则返回0。原型是int eof();

1
2
3
4
if(infile.eof())
{
cout<< "已经到达文件尾!" << endl;
}


const和static const的区别 @2016年5月2日16:11:21

对于C/C++语言来讲,
const就是只读的意思,只在声明中使用;
static一般有2个作用,规定作用域和存储方式。
对于局部变量,static规定其为静态存储方式,每次调用的初始值为上一次调用的值,调用结束后存储空间不释放;
对于全局变量,如果以文件划分作用域的话,此变量只在当前文件可见;
对于static函数也是在当前模块内函数可见。
static const 应该就是上面两者的合集。

下面分别说明:

全局:
const,只读的全局变量,其值不可修改。
static,规定此全局变量只在当前模块(文件)中可见。
static const,既是只读的,又是只在当前模块中可见的。

文件:
文件指针可当作一个变量来看,与上面所说类似。

函数:
const,返回只读变量的函数。
static,规定此函数只在当前模块可见。


double数组进行递增排序算法 @2016年5月5日11:32:14

代码示例

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
int Cmp(const void *a, const void *b)
{
return *(double *)a > *(double *)b ? 1 : -1;
}

void CalcSample_H(int id)
{
double *h_all = new double[x_sp.size()];
for (int i = 0; i < x_sp.size(); i++)
{
double h_temp = 0.0;
if (DIM == 2)
{
h_temp = sqrt((x_sp[id].x[0] - x_sp[i].x[0])*(x_sp[id].x[0] - x_sp[i].x[0]) +
(x_sp[id].x[1] - x_sp[i].x[1])*(x_sp[id].x[1] - x_sp[i].x[1]));
h_all[i] = h_temp;
}
if (DIM == 3)
{
h_temp = sqrt((x_sp[id].x[0] - x_sp[i].x[0])*(x_sp[id].x[0] - x_sp[i].x[0]) +
(x_sp[id].x[1] - x_sp[i].x[1])*(x_sp[id].x[1] - x_sp[i].x[1]) +
(x_sp[id].x[2] - x_sp[i].x[2])*(x_sp[id].x[2] - x_sp[i].x[2]));
h_all[i] = h_temp;
}
}
qsort(h_all, x_sp.size(), sizeof(h_all[0]), Cmp); // 从小到大排序
x_sp[id].h_min = h_all[1]; // 取第二个h作为该点的最小h,因为第一个是到该点本身的距离,为0
delete[]h_all;
}


C++ 函数参数的传递方式 @2016年5月14日13:57:47

今天写个小程序时犯了个错误,具体表现为:在主函数里声明了一个变量,然后将这个变量传递给一个子函数,这个变量在子函数里进行赋值操作,然后在主函数里接着对该变量进行操作(本意是想对赋值后的变量进行操作的),结果当然是该变量只是初始化的状态,子函数中对该变量的赋值操作并不会对主函数中该变量产生影响。这要从函数参数的传递方式来分析:

(1)值传递:
形参是实参的拷贝,改变形参的值并不会影响外部实参的值。从被调用函数的角度来说,值传递是单向的(实参 —> 形参),参数的值只能传入,不能传出。当函数内部需要修改参数,并且不希望这个改变影响调用者时,采用值传递。

(2)指针传递:
形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。

(3)引用传递:
形参相当于是实参的「别名」,对形参的操作其实就是对实参的操作,在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。

下面是之前的错误代码,贴上来做个备忘

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
int main(int argc, char* argv[])
{
vector<Elements> elementVector; //1、声明一个变量
ReadElemet(elementVector); //2、传递给子函数(值传递)

//4、主函数中再调用,该变量并没有赋值
cout << elementVector.size() << endl;
system("pause");
return 0;
}

vector<Elements> ReadElemet(vector<Elements> elementVector)
{
Elements tempElem;

ifstream infile;
infile.open("elem_rat.txt");
if (!infile.is_open())
{
cout << "Can not open \"elem_rat.txt\"" << endl;
return;
}
while (!infile.eof())
{
string lineStr = "";
getline(infile, lineStr);
if (lineStr != "")
{
cout << lineStr << endl;
sscanf(lineStr.c_str(), "%d,%d,%d,%d,%d",
&(tempElem.id),
&(tempElem.node[0]),
&(tempElem.node[1]),
&(tempElem.node[2]),
&(tempElem.node[3]));

// 3、子函数中做赋值操作,不影响主函数中的变量
elementVector.push_back(tempElem);
}
}
}