关键词不能为空

当前您在: 主页 > 英语 >

VS-C++2010入门教程

作者:高考题库网
来源:https://www.bjmy2z.cn/gaokao
2021-03-03 12:56
tags:

-

2021年3月3日发(作者:数量的英文)



第四章


VC2010

中初学者常见错误、警告和问题





这一章将帮助大家解释一些常见的错误、


警告和问题,


帮助大家去理解和解决一些常

见问题,并了解它的根本原因。




iostream.h






下面的代码 为什么在


VC2010


下面编译不过去?



#include


int main()


{


cout<<



return 0;


}



错误信息:


fatal error C1083:


无法打开包括文件


:“iostream.h”: No such file or


directory



造成这个错误的原因在于历史原因,


在过去

C++98


标准尚未订立的时候,


C++

< br>的标准


输入输出流确实是定义在这个文件里面的,这是


C


风格的定义方法,随着


C++98


标准 的确


定,


iostream.h


已经被 取消,至少在


VC2010


下面是这样的,取而代之的是我们要 用



头文件来代替,你甚至可以认为



是这样定义的:




namespace std



{



#include



}



因此我们可以简单的修改我们的


Hello World





#include


using namespace std;


int main()


{


cout<<



return 0;


}



iostream .h


是属于


C++


的头文件,


而非


C


的,


因此标准订 立的时候被改成了




C


的头文件


stdio.h


等依然可以继续使用,


这是为了兼容


C


代码。


但是它们依然有对应的


C++


版本,




等。


记住,


VC2010


上面采用


C++


风格 的头文件而不是


C


风格的头文件,除非你是在用


C





warning C4996




这是一个警告,请看下面的代码:



#include


using namespace std;


int main()


{


char sz[128] = {0};


strcpy( sz,


cout<< sz << endl;


return 0;


}



上面的


strcpy


会产生这个警告< /p>


:



warning C4996: 'strcpy': This function or variable may be unsafe. Consider using


strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See


online help for details.



这是因 为


VC



2005

版本开始,


微软引入了一系列的安全加强的函数来增强


CR T



C


运行时),这里对应的是


strcpy_s



_s

意为


safe


的意思,同样的道理,


strcat


也是同样。


因此要解决这个问题,我们可以用< /p>


strcpy_s


来替换


strcpy< /p>


,但是注意


strcpy_s


并非所有编


译器都提供,因此如果要跨编译器,请采用错误信息中所提示的方式,定义


_CRT_SECURE_NO_WARNINGS


宏来掩耳盗铃吧。


另外注意并非所有的加强函数都是在


屁股后面加


_s


,比如


stricmp


这个字符 串比较函数的增强版名字是


_stricmp


。下面,用


strcpy_s


来更改程序:



int main()


{


char sz[128] = {0};


strcpy_s( sz,


cout<< sz << endl;


char* pSz2 = new char[128];


strcpy_s( pSz2, 128,


cout<< pSz2 << endl;


delete pSz2;


return 0;


}



注意,


strcpy_s


有两个版本,一个可以帮助我们自动推断缓冲区的大小 ,而另外一个


不能帮助我们推断,


因此在编译器不能推断缓冲区 大小的时候,


我们需要自己指定缓冲区的


大小,如上面的程序所 演示的那样,关于增强版的函数请参考我写的


《深入学习


C++


String2.1


版》。




TCHAR



wchar_t



char



请大家看下面这个程序:



#include


#include


#include


using namespace std;


int main()


{


MessageBox( NULL, < /p>


你好


HelloWorld


< p>


return 0;


}



貌似没什么问题吧?错了,如果你是按照我教你 的方法创建的控制台空工程的话,那


么会有编译错误:




error C2664: “MessageBoxW”:


不能将参数



2



“const char [17]”


转换为


“LPCWSTR”




这个问题太普遍了,几乎所有的初学者都会遇到 而且感到难以应付,因为按照提示使



(LPCWSTR)


强制转型貌似并不能帮助我们解决问题,


而且这个程序在

< p>
VC6


下面应该是没


有任何问题的,


那问题出现在哪里呢?问题在这里,


请右键单击解决方案浏览器下面的项目,


属性,



< p>
问题的根本就是字符集问题,在


VC6


中,


我们默认使用的是多字节字符集,而现在我


们默认需要的是

< p>
UNICODE


字符集,简单的,我们把这个字符集改成多字节字符集这个 问


题就解决了:






再试试应该 就可以了吧?但是我并不推荐大家这么做,因为让自己的程序适应各种


字符集是我们写代 码的人义不容辞的义务。




我们把程序改成下面这样:



#include


#include


#include


using namespace std;


int main()


{


MessageBox( NULL, T EXT(


你好


HelloWorld




MessageBox( NULL, _T(


你好


HelloWorld




return 0;


}



用两个宏


TEXT


或者


_T


都可以解决这个问题,它们两个并没有太大区别,也许区别在


于前者是通过


windows.h


头文件引入的,而


_T


是通过


tchar.h

< p>
引入的,我推荐大家使用


_T


< br>tchar.h


,因为


tchar.h

< br>还帮助我们引入了其它一些很有用的宏,比如


_tcscpy_s


,这个宏


在使用


UNICODE


字符集的时候被替换成


wcscpy_s


< br>在使用多字节字符集的使用被替换成


strcpy_s


。 关于这部分的内容,请大家不要错过



Windows


核心编程》的第二章


(


第四版或


第五版都可以),


以及


《深入学习

C++ String2.1


版》。



它们都有提到。




有人听说


_T


可以把多字节字符串转换成< /p>


UNICODE


,因此他写了如下的代码:



const char* pStr =


哈哈



MessageBox( NULL, _T(pStr), _T(



当然,除 非你运气好的抓狂,否则你是编译不过去的,为什么呢?我们现在应该知道对




这样的字符串,


VC2010

< br>会默认的将它视为


const char*


,即多字节字 符串,而


L


前面有个


L


前缀的被视为


UNICODE


字符串,这和

< p>
C#


是有区别的,因为


C#


的字符


串总是被视为


UNICODE



C++/CLI


下面编译器也会帮助我们做到这件事情,


所以它们不需



L(C++/CLI


兼容


L


这种写法)。




让我们看看


_T

< p>
的定义吧:



#define wxCONCAT_HELPER(text, line) text ## line


/* could already be defined by tchar.h (it's quasi standard) */


#ifndef _T



#if !wxUSE_UNICODE



#define _T(x) x



#else /* Unicode */



/* use wxCONCAT_HELPER so that x could be expanded if it's a macro */



#define _T(x) wxCONCAT_HELPER(L, x)




#endif /* ASCII/Unicode */


#endif /* !defined(_T) */



_T


< p>
UNICODE


下面最终会被替换成


L ## x




##


是 一个编译预处理指令,意味着让


L



x


贴在一起,


比如


L ##

< p>
最终就是


L



因此它可以 把



转换成


UNICODE


字符串。那为什么上面的程序不行呢?让我们看看


_T(

会被替换成什么:


L ## pStr ->


LpStr


,哦,


LpStr


是一个新的标识符, 如果你没有定义过它,你当然不能通过编译啦。




因此我们可以了解到


_T


这样的宏只能 处理


直接的常量字符串


,不能处理其它的情况。


而我们上面演示的那种情况需要我们动态的去转换编码,


Windows



API


可以帮助我们做

到,


C


库也有函数可以帮助我们。恰好我曾经写过这样的代 码,欢迎大家参考:


ASCII/UNICODE/UTF8


字 符串互相转换的


C++


代码




对于


_T

< br>宏,


再说一点东西,


或许你会感到奇怪为什么

< p>
_T


不直接定义成


#define _T(x) L ##


x



而要绕个圈子去调用< /p>


wxCONCAT_HELPER


呢?这实际上涉及到宏展开顺序 和截断的问


题。在这里,我们需要说一个


宏参数


的概念,这和函数的参数是类似的,这里


_T(x)



x



是宏参数,好,记住下面一句话:




如果你定义的宏中使用了


#


或者是


##


的话,宏参数将不会被展开


,也就是说


_T(x)


如果直


接定义成


L##x


那 么在下面这种情况就会出错


( PS: #


是给参数加引号的意思):




_T(__FUNCTION__)



__FUNCTION__


是一个预定义的宏,它代表了当前函数的名字,


这个展开会是什么呢?


L__FUNCTION__


。为什么间接调用


wxCONCAT_HELPER


就能得


到正确的结果呢?因为当我们调用


wxCONCAT_HELPER


的时候,


__FUNCTION__


已 经被


_T


展开成了函数名。


/*


即参数如果是宏,则不能得到正确结果;如果是直接的参数,则可


以< /p>


*/



说多了说多了,如 果你觉得复杂可以暂时跳过这些东西,我只是顺便说说。






重定义的编译错误和链接错误




让我们在项目里面再添加一个


Test.h

头文件,


方法是右击解决方案中的项目,添加,



建项,


C++


头文件,名称输入


test.h


。然后我们在


test.h


中输入:



/*#pragma once*/


void print()


{


}



回到



中:



#include


using namespace std;


#include


#include


int main()


{


return 0;


}



编译一下我们会得到重定义的编译错误:




error C2084:


函数


“void print(void)”


已有主体




或许你会说,


你引用


(#includ e)


了两次,


我没你那么傻,


我只引用 一次不就好了么?是的。


你聪明,但是是小聪明哈,因为你不能保证每个人都不去引用它 。




这个问题演示的是


#pragma once

< br>的用处,让我们解开它的注释。编译成功!


#pragma

once


的作用就在于防止头文件被多次引用。你或许见过



#ifndef __TEST_H__


#define__TEST_H__


代码



#endif



这样的代码,它们的作用是一样的,如果你跟我一样懒 ,那么就用


#pragma once


,如果

< br>你打算去没有这个指令的编译器上编译代码,那么还是用后面一种方式吧。




现在让我们来见识一个对初学者稍微复杂一点的链接 错误,用创建



的方法再


添加一个


test.h


头文件,输入


#include


即可。





让我们再编译一次。



1> : error LNK2005:


已经在




中定义



1>e:documentsvisual studio : fatal


error LNK1169:


找到一个或多个多重定义的符号




如果说编译错误好找的话,链接错误对于初学者来说就有点麻烦了,聪明的初学者会



Google


、百度寻找答案,笨的初学 者就会找所谓的高手、前辈问,而这些高手


Or


前辈

< p>
未必有心情为你解释。要解决这个错误有无数种方法。




1.


内联,把


print


声明为内联函数。




inline void print()



{



}



这个方法的好处是简单,坏处是局限性太强,意味着你总是需要公开

print


的实现,


因为内联函数必须在编译时就知道实现 才行。





,把


print


声明为


sta tic


函数:




static void print()





这便告诉编译器,哥是唯一的,而且哥只能被本编译单元的代码调用,这和


extern


是对应的。简单来说,想要哥帮你做事,请先

< br>include


哥声明的头文件,也就是


#includ e






3..h


头文件中只放声明,实现放到


.cpp


中去。




现在


test.h


中只有


void print();


,而实现在


< p>
中:




#include



void print()



{



int a = 1;



cout<< a++ << endl;



}



这个时候有意思的是我们在



无需包含


test.h


头文件 也可以引用


print


函数,


因为


print


并非


static


的函数:



void print();


int main()


{


print();


print();


return 0;


}



但是声明一下是必须的。




由于百度空间的帖子的篇幅是有限制的,因此今天只好就说这么几点了。新的内容请


大家等候下一章。



合理组织项目、使用外部工具让工作更


...




这一章跟大家分享一 些与


c++


项目管理、


VAX



SVN



VS


快捷键等方面的东西。




有效的在项目中组织


C++


文件,分配各种 文件的目录对以后的维护会有好处的,至少


不会出现不知道什么东西在什么地方,特别是 大的项目,这里用


T


extSearcher

< br>来做例子。




使 用


SVN


来管理项目会让我们的工作更轻松,工作也会更简单容 易。




掌握常用的快捷键 和常用的


VS


功能让我们的工作更有效。




合理的组织文件体系




首先说在


IDE


中为我们的文件分类组 织,如下图所示:




我把不同功能的 代码和文件放在不同的


Filter


下面,如何添加这样的


Filter


呢?





这样就可以添加筛选器 了,默认情况下


VS


为我们创建三个筛选器:头文件、源文件和


资源文件,实际上我们可以再增加很多。这样区分开的好处就是各个功能的代码被分开了 ,


在文件很多的情况下不会造成混乱。如


TextSearch er


,它的搜索算法、软件控制逻辑、自


定义控件、

< p>
GUI


模块、


线程化操作都是分开的,

< p>
这样我可以很容易找到我想找的文件,而且


还可以检视自己的模块划分是否 合理等等。




接下来推荐大家在项目资源浏览器中为不同的文件划分目录。




比如上图将头文件和源文件、


资源图 标文件、


本地化文件和配置文件分开组织,


这样也是为


了防止混乱。


值得注意的是当我们把文件用文件分开的时候,

< p>
需要在项目属性设置里面包含


我们的子目录,


否则 我们无法在源文件中直接用


#include


指令包含我们的头 文件。


如下图所


示这样的情况,如果不添加,无法找到头文件。






如下图所示,找到项目属性中,


C++


,常规中把我们的子目录作为附加路径添加到




加包含目录


中。




使用


SVN


或其它源代码管理工具管理我们的项目




如果你打算写一个比较大一点的项目,我推荐 你使用源代码管理工具来管理你的


C++


项目,你可以选择


SVN


,也可以选择其它的,我推荐


SVN


,因为简单容易上手。




当你在做一个很复杂的东西的时候,花了两三天的时间去做修改,不过后来发现这个


修改并不合适,


想还原到三天以前,


如果你 没有用源代码管理工具管理自己的项目也没有自


己手动的备份,


那恭喜你,你得开始人肉还原了,


这是多么悲剧的一件事情啊,然而如果你


使用了


SVN


管理的话,只需要在三天前开始修改前 的最后一次稳定版本


Commit


一次,三

天之后如果要还原,只需要使用工具


Revert


就好了, 而且不但可以回到三天前的版本,你


甚至可以回到以前每一次


C ommit


的版本,


(



o




)


哇,这是多么好的工具啊!




要使用


SVN


,首先需要


SVN


客户端,

< br>SVN


服务器是可选的。我推荐大家使用


Tortois eSVN


这个


SVN


客户端,因为它简 单易用、免费,支持


Windows32Bit



64Bit


,你


可以去他们的官方网站下载,地址点 我。有了这个工具当然还不够,为了让我们的


SVN



VS2010


结合的更紧密,我们需要再下载一个


SVN For VS


的插件,我强烈推荐你使用它,


因为它 也非常简单易用,下载地址点我。最新版是支持


VS2010


的 。当你装了这个插件的


时候,如果你的项目是在


SVN


的管理之下,那么你的项目看起来会一些不同:


















< br>表




















PendingChanges

< p>
则告诉我们哪些文件时新加的,


哪些文件被改过了,


如果要还原的话,



需要选择该文件,右键点击,

< p>
Revert


就好了:





如果你已经安装好了


A nkhSVN


插件但是又看不到的话,


那么请检查下系统选项卡 里面


是否选择了它作为默认的源代码管理工具:





回过头去继续说


SVN


,当我们安装好

TortoiseSVN


之后(安装后可能需要重启)


,我 们在资


源管理器中点击右键的时候就能看到它的菜单了:





虽然

< br>TortoiseSVN


有中文语言包,


但是我推荐大家 使用英文的,


就像古诗一定要用中文


来表达一样,没有比英语单 词表达


SVN


项目管理更恰当的词了。




SVN CheckOut

可以让你获取其它地方


SVN


服务器上面的某个项目的源码 ,当然,前


提是你要有权限才行,现在让我们试试。随便找个盘符如

F


盘,点右键,选择



SVN


CheckOut


,然后


Url of Repository


中输入


svn:///TextSear cher


这个地址,


其它的不变,点击


OK


。如图所示:





如果不出意外你能看到:





















CheckOut< /p>









< p>








TextSearcher


的源码,当然要说一点的是,这个源码可能你取到之后编 译不了,因为还有


另外一些依赖项如


dbsoft



boost



wxWid gets


并不在这个


SVN


上面。




这样大家就可以去一些开 源的网站上面


Check


你想要的东西了,哈哈。推荐大家去这


个网站找自己感兴趣的代码


CheckOut

< br>:


/



现在我们说 如何管理自己的项目,大家可以像我一样去一些提供免费


SVN


服务器服务


的网站上面注册一个账号建立项目就好了,比如说我使用的这个



,这个毕


竟是国内的,另外上面的

< br>


也可以。当你注册建立项目成功之后就可以通过


菜单


Import


把需要导入的东西导入到


SV N


服务器了:






< br>界












SVN














svn:/// TextSearcher


。导出的时候记得输入日志。除了可以导入

< br>SVN


服务


器之外,


我们还可以 使用文件协议在自己的电脑上管理我们的代码,


如果你不需要在多台电

< br>脑上面共同维护这个项目的话。




让我们随便找个地方新建一个文件夹,如


D:TestSVNServe r


这个文件夹,然后对着这


个新建的文件夹点右键,选择


SVN>Create repository here



OK


,你的本地


SVN


服务器已


经建好了。如果创建成功,你会发现这个文件夹里面多了好多东西:

< p>




没关系, 这是


SVN


服务必须要的一些东西,现在让我们使用文件协议导 入我们的项目


到该


SVN


服务器中,找 到我们要导入的项目,跟导入网络上的


SVN


服务器一样,右击 项


目文件夹,


SVN>Import


。 在


Import


界面中这样填:





< p>
不出意外你会导入成功,


那么现在我们需要去其它地方重新获取这些东西了 ,


换个地方,


比如


F

< br>盘根目录,选择


SVN CheckOut


,然后地址输入刚才导入的地址:













< br>不













F












D:TestSVNServer


文件夹,把


D:


这个


SVN


帮我们自己填充的路径删掉然后点确定。






现在我们已经


CheckOut


了刚才导入的项 目了,


Planet


文件夹已经带上了一个绿色的勾。


点击这个


Planet


目录,选择


SVN>Show Log


,你会看到:





在这个 界面我们还可以检查代码的改变都是什么,这里由于是新的项目,所以没有更


改,我们可 以找到


T


extSearcher


的更 改来比较它们的改动都是什么:



-


-


-


-


-


-


-


-



本文更新与2021-03-03 12:56,由作者提供,不代表本网站立场,转载请注明出处:https://www.bjmy2z.cn/gaokao/700109.html

VS-C++2010入门教程的相关文章