社区应用 最新帖子 精华区 社区服务 会员列表 统计排行 银行

  • 28164阅读
  • 17回复

Visual Studio2008使用技巧和快捷键(VS2008,VS2010)

级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
1,Visual Studio 2008自带的1000多个 Windows 系统使用的各种图标、光标和动画文件
在Visual Studio 2008的安装目录下,

\Microsoft Visual Studio 9.0\Common7\VS2008ImageLibrary\2052文件夹下面,有一个VS2008ImageLibrary.zip,这个文件里面将常用的Windows系统图标、光标和动画文件集中到了一起。包括Office、Win9x、WinVista和WinXP等系统使用的,可以免费使用哦。  


2,删除多余的using指令,并且排序

当我们新一个类的时候,Visual Studio 会将常用的命名空间using在类的头部。但是在我们写完一个类的时候,有些using是多余的,删除多余的using,再排一下序,可以使代码看起来更清晰。Visual Studio 2008 已经为我们做好了这一切。在代码编辑区点击右键,可以看到"组织 using"菜单,这就是我们需要的了。


3,复制或删除一行代码时,不用先选择它

如果你想复制一行代码,你只需要简单的按CTRL+c拷贝,然后按CTRL+v粘贴这行就可以了,而不需要选择整行的代码。如果你想删除一行代码,只需按CTRL+x就可以了。


4,取代其他编辑器里Ctrl+F的方便的增量查找

用过ultraedit会对那两个上下搜索按钮印象深刻,Visual studio里有更厉害的。

操作方法:  

1).按Ctrl+I键;  

2).键入你要搜索的文本。注意:这时你会看到光标跳至第一个匹配的地方,匹配的文本高亮显示;  

3).再次按下Ctrl+I键,光标将跳至下一个匹配的文本;  

4).按Ctrl+Shift+I可向后搜索;  

5).要停止搜索,按ESC键;  


5,如何在编辑器中进行框式选择

操作方法:  

你是否知道VS提供了两种不同的选择模型:流式和框式吗?  

大家应该都熟悉流式选择模型了,只要使用Shift+方向键即可(或者使用鼠标进行选择)。  

框式选择则允许你同时对航和列进行选择。只要同时按下Shift+Alt+方向键,你就了解它的不同之处了。剪切,拷贝,粘贴,这些功能都能使用,只是需要记住从哪里开始选择的。啊,让我想起了那段做测试时的时光。  

备注:也可以使用鼠标+Alt键完成该操作。前两天我还遇到该提示的一个有意思的用法。有时候我们拷贝网上的文章中的代码时会将行号一起拷进来,使用框式选择可以只选择行号部分,将其删除。


6,如何使用快捷键在当前代码行的上面或下面插入一行(推荐,很实用)

按下Ctrl+Enter会在上面插入一个空行,Ctrl+Shift+Enter则会在下面插入一个空行。光标会移至新行的开始处。  

备注:这是我非常喜欢的一个快捷键,如果不用这个,则需要使用Home或End,然后使用方向键,再使用回车才能达到上面的效果。


7,安装之后,将您的IDE设置恢复到默认设置

如果IDE的设置在任何先前发布的版本中做了更改,它们都应该被恢复到默认设置。可以在Visual Studio 2008中点击菜单 Tools > Import and Export Settings... > Reset all settings,此外还有一些Import和Export的选项可用。


8,通过按"tab"两次插入代码块

操作步骤:

在编辑的中输入代码片段,比如"for"


在这个状态下,按两次"tab"键,将会插入代码块,如下图所示


此时你可以把第一"i"改成"j",然后按Tab键,则所有的变量都变成了"j"。

备注:这样既快又不容易语法出错。


9,使用Ctrl+Tab打开IDE的导航,获得鸟瞰视图

同时在Visual Studio中导航到所有打开的文件和工具窗体

按"Ctrl+Tab"键,打开IDE导航窗口,按住Ctrl键,同时用方向键或鼠标选中一个文件或工具窗体来激活。


备注:这时最好不要松开"Ctrl+Tab",按方向键看鸟瞰图,全部松开后就定位到需要的文件或工具窗体,说实在的,这窗口挺酷的。


10,查找匹配的标记

某些标识总是成对出现。例如,"{"标识必须用对应的"}"标识关闭。虽然你点击一个{ 和它匹配的}就会高亮显示,但是如果代码过长的话就不好找了,同样,编译器指示符"#region"必须有对应的"#endregion"指示符。当导航你的代码时,你有时需要查找对应的标识。通过按Ctrl-]你可以这样做。这个快捷键只有当光标在这些标识符的任何一个的下面时才起作用,它会立即跳转到对应的标识符而不管它是开的或闭的标识。

如果你想显亮两个匹配的标识之间的所有代码时,按Ctrl-Shift-]显亮整个块,并移动光标到开的标识处。这个快捷键只有当光标在任意的标识的下面时才起作用(如光标在区域内它就不会起作用了)

11.如果你想临时想禁用一段代码,你可以把这段代码注释掉,方法是:先选择这段代码,然后CTRL+k+c(按住CTRL键不放,然后,按一下k键,再按一下c键),见(图5)(图6)。使用CTRL+k+u可以取消注释

12 - 使用CTRL+n或CTRL+SHIFT+a创建新项

13 使用CTRL+空格进行自动完成
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 沙发  发表于: 2011-11-07
Shift+Alt+Enter: 切换全屏编辑

Ctrl+m+Crtr+o折叠所有大纲


Ctrl+M+Crtr+P: 停止大纲显示


Ctrl+K+Crtr+C: 注释选定内容


Ctrl+K+Crtr+U: 取消选定注释内容


Ctrl+J : 列出成员 智能感知


Ctrl+B,T / Ctrl+K,K: 切换书签开关


Ctrl+B,N / Ctrl+K,N: 移动到下一书签


Ctrl+B,P: 移动到上一书签


Ctrl+B,C: 清除全部标签

Ctrl+I: 渐进式搜索


Ctrl+Shift+I: 反向渐进式搜索


Ctrl+F: 查找


Ctrl+Shift+F: 在文件中查找


F3: 查找下一个


Shift+F3: 查找上一个


Ctrl+H: 替换


Ctrl+Shift+H: 在文件中替换


Alt+F12: 查找符号(列出所有查找结果)


Ctrl+Shift+V: 剪贴板循环


Ctrl+左右箭头键: 一次可以移动一个单词


Ctrl+上下箭头键: 滚动代码屏幕,但不移动光标位置。


Ctrl+Shift+L: 删除当前行


Ctrl+M,M: 隐藏或展开当前嵌套的折叠状态


Ctrl+M,L: 将所有过程设置为相同的隐藏或展开状态


Ctrl+E,S: 查看空白


Ctrl+E,W: 自动换行


Ctrl+G: 转到指定行


Shift+Alt+箭头键: 选择矩形文本


Alt+鼠标左按钮: 选择矩形文本


Ctrl+Shift+U: 全部变为大写


Ctrl+U: 全部变为小写代码快捷键


Ctrl+Shift+空格键 / Ctrl+K,P: 参数信息


Ctrl+K,I: 快速信息


Ctrl+E,U / Ctrl+K,U: 取消选定注释内容


Ctrl+K,M: 生成方法存根


Ctrl+K,X: 插入代码段


Ctrl+K,S: 插入外侧代码


F12: 转到所调用过程或变量的定义窗口快捷键


Ctrl+W,W: 浏览器窗口


Ctrl+W,S: 解决方案管理器


Ctrl+W,C: 类视图


Ctrl+W,E: 错误列表


Ctrl+W,O: 输出视图


trl+W,P: 属性窗口


Ctrl+W,T: 任务列表


Ctrl+W,X: 工具箱


Ctrl+W,B: 书签窗口


Ctrl+W,U: 文档大纲


Ctrl+D,B: 断点窗口


Ctrl+D,I: 即时窗口


Ctrl+Tab: 活动窗体切换


Ctrl+Shift+N: 新建项目


Ctrl+Shift+O: 打开项目


Ctrl+Shift+S: 全部保存


Shift+Alt+C: 新建类


Ctrl+Shift+A: 新建项


Shift+Alt+Enter: 切换全屏编辑


Ctrl+B,T / Ctrl+K,K: 切换书签开关


Ctrl+B,N / Ctrl+K,N: 移动到下一书签


Ctrl+B,P: 移动到上一书签


Ctrl+B,C: 清除全部标签


Ctrl+I: 渐进式搜索


Ctrl+Shift+I: 反向渐进式搜索


Ctrl+F: 查找


Ctrl+Shift+F: 在文件中查找


F3: 查找下一个


Shift+F3: 查找上一个


Ctrl+H: 替换


Ctrl+Shift+H: 在文件中替换


Alt+F12: 查找符号(列出所有查找结果)


Ctrl+Shift+V: 剪贴板循环


Ctrl+左右箭头键: 一次可以移动一个单词


Ctrl+上下箭头键: 滚动代码屏幕,但不移动光标位置。


Ctrl+Shift+L: 删除当前行


Ctrl+M,M: 隐藏或展开当前嵌套的折叠状态


Ctrl+M,L: 将所有过程设置为相同的隐藏或展开状态


Ctrl+M,P: 停止大纲显示


Ctrl+E,S: 查看空白


Ctrl+E,W: 自动换行


Ctrl+G: 转到指定行


Shift+Alt+箭头键: 选择矩形文本


Alt+鼠标左按钮: 选择矩形文本


Ctrl+Shift+U: 全部变为大写


Ctrl+U: 全部变为小写
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 板凳  发表于: 2011-11-09
集成VS2008命令行编译到.sln文件的右键菜单
背景:VS2008很好很强大,但太费系统资源了,尤其是在虚拟机在里面装VS2008的时候更是如此。有时用vi编辑了源代码后,不想开VS IDE编译,但每次打开VS2008命令行,再切换到工程所在目录,然后手动敲命令太麻烦了。于是产生了集成集成VS2008命令行编译到.sln文件右键菜单的想法。


先上效果图:




1) VS2008命令行编译的命令:
view plainprint?

    // 编译Debug版本  "C:\Program Files\Microsoft Visual Studio 9.0\VC\vcpackages\vcbuild.exe" xxx.sln "Debug|Win32"  
2) 仅集成到.sln文件
HKEY_CLASSES_ROOT\.sln, 右边的默认值为: VisualStudio.Launcher.sln
==> 在  HKEY_CLASSES_ROOT\VisualStudio.Launcher.sln\Shell 下面增加一个子项即可添加右键菜单
3) 注册表集成右键菜单时,%1代表该文件名。
view plainprint?

    "C:\Program Files\Microsoft Visual Studio 9.0\VC\vcpackages\vcbuild.exe" %1 "Debug|Win32"  
但直接执行该命令的时候,cmd窗口闪一下就消失了,看不到编译结果。


4) 利用 cmd /K 命令,在执行了以上命令后保持命令行窗口不退出,以便查看编译结果。
     最终命令为:
view plainprint?

    cmd /K ""C:\Program Files\Microsoft Visual Studio 9.0\VC\vcpackages\vcbuild.exe" %1 "Debug|Win32""  


【小结】  新建一个文本文件,将以下内容保存以下内容,然后将文件名修改为 anyname.reg ;双击导入该reg文件到注册表;杀掉explorer进程再启动explorer进程(或注销或重启机器)即可。
(VS2008的路径请自行调整)
view plainprint?

    Windows Registry Editor Version 5.00    [HKEY_CLASSES_ROOT\VisualStudio.Launcher.sln\Shell]    [HKEY_CLASSES_ROOT\VisualStudio.Launcher.sln\Shell\buildDebug]  @="命令行编译(Debug)"    [HKEY_CLASSES_ROOT\VisualStudio.Launcher.sln\Shell\buildDebug\command]  @="cmd /K \"\"C:\\Program Files\\Microsoft Visual Studio 9.0\\VC\\vcpackages\\vcbuild.exe\" %1 \"Debug|Win32\"\""    [HKEY_CLASSES_ROOT\VisualStudio.Launcher.sln\Shell\bulildRelease]  @="命令行编译(Release)"    [HKEY_CLASSES_ROOT\VisualStudio.Launcher.sln\Shell\bulildRelease\command]  @="cmd /K \"\"C:\\Program Files\\Microsoft Visual Studio 9.0\\VC\\vcpackages\\vcbuild.exe\" %1 \"Release|Win32\"\""  
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 地板  发表于: 2011-11-18
使用VS2008 功能包加强Windows 应用程序
[font='segoe]
Kenny Kerr


本文以 Visual C++ 功能包的预发布版为基础。文中包含的所有信息均有可能变更。
本文讨论:
Microsoft 基础类库更新
使用 C++ 对功能区进行编程
使用 C++ 实现选项卡式 MDI
多态函数和智能指针
本文使用了以下技术:
Visual Studio 2008,MFC
   目录
Office 功能区用户界面
选项卡式多文档界面
标准 C++ 库中的新功能
多态函数对象
智能指针
作为一名 Visual C++ 开发人员 ,您在最近几年可能有点被冷落的感觉,因为与 Visual C#? 相比,似乎 Microsoft 向 Visual C++? 添加的新特性和新功能要少很多。事实上,尽管 Visual C++ 编译器在性能、安全性和标准符合性等方面始终在不断改进,但很长时间以来在新库和生产率功能方面却做的比较少。后来虽然更新了 MFC 以更好地支持 Windows Vista?,但仍有许多工作需要完成。
但是现在,为了对那些使用本机代码和 MFC 的开发人员提供更好的支持,Microsoft 发布了 Visual C++ 2008 功能包。以下是 Visual C++ 更新的一些主要内容。
此功能包包括用于构建现代用户界面的一大组新 MFC 类。它还包括作为技术报告 1 (TR1) 的一部分添加到标准 C++ 库中的大量功能。TR1 是 C++ 委员会所采用的首个针对标准 C++ 库的主要更新和添加内容。
多年以来,传统模式的单个和多个文档/视图应用程序、菜单、工具栏和对话框一直是 MFC 开发的一个主要方面。如果想让 MFC 应用程序看起来更现代一点,用户需要自己想办法。
现在则完全不同了。MFC 现在包括了许多新的用户界面模式,其中甚至还有类似于 Microsoft? Office 和 Visual Studio? 中的可停靠窗格。它还完全支持 Microsoft Office 功能区用户界面以及众多其他新控件、对话框和窗口等。
接下来,我将演示 MFC 中的两个新用户界面功能:Office 功能区和选项卡式多文档界面 (MDI)。


Office 功能区用户界面
到目前为止,我相信您已见过了新的 2007 Microsoft Office 系统功能区元素,并且您可能想知道如何在自己的应用程序中营造出这种效果。令人高兴的是,现在可以非常轻松地向 MFC 框架窗口添加功能区栏。
许多新功能都依赖于新版本的 CwinApp、CFrameWnd 和 CMDIFrameWnd 类;这些类代表着大多数 MFC 应用程序的基础。CWinAppEx 由 CwinApp 派生而来,应该用作应用程序对象的基类。CFrameWndEx 由 CframeWnd 派生而来,应该用作单文档界面 (SDI) 框架窗口的基类。同样,CMDIFrameWndEx 由 CMDIFrameWnd 派生而来,应该用作 MDI 框架窗口的基类。这些新的基类提供了支持众多新用户界面功能(如可停靠、可调整大小的窗口窗格以及工作区持久性等)所需的全部要素。
图 1 显示了可支持功能区栏的最小应用程序对象。如您所见,Application 类由 CwinAppEx 派生而来,可实现大家所熟悉的 InitInstance 成员函数(通常用于创建应用程序的主窗口)。千万不要忘记调用 SetRegistryKey 成员函数来设置应用程序设置的注册表位置,因为框架类要依赖于它。然后,InitInstance 继续以通常的方式创建主窗口。
   Figure 1 功能区应用程序对象
class Application : public CWinAppEx
{
public:


    virtual BOOL InitInstance();


};


BOOL Application::InitInstance()
{
    SetRegistryKey(L"SampleCompany\\SampleProduct");


    m_pMainWnd = new MainWindow;
    m_pMainWnd->ShowWindow(m_nCmdShow);
    m_pMainWnd->UpdateWindow();


    return TRUE;
}


图 2 中的代码显示了具有功能区栏和应用程序按钮的一个最小 SDI 框架窗口。应用程序按钮并不是必需的,但通常会与功能区栏结合使用,为应用程序提供各种各样的主菜单,以代替传统的“文件”菜单。
   Figure 2 功能区框架窗口
class MainWindow : public CFrameWndEx
{
    DECLARE_MESSAGE_MAP()


public:


    MainWindow();


private:


    int OnCreate(CREATESTRUCT* createStruct);


    CMFCRibbonBar m_ribbon;
    CMFCRibbonApplicationButton m_appButton;
};


BEGIN_MESSAGE_MAP(MainWindow, CFrameWndEx)
    ON_WM_CREATE()
END_MESSAGE_MAP()


MainWindow::MainWindow()
{
    Create(0, // class name
        L"MFC Ribbon Sample Application");
}


int MainWindow::OnCreate(CREATESTRUCT* createStruct)
{
    if (-1 == __super::OnCreate(createStruct))
    {
        return -1;
    }


    if (-1 == m_ribbon.Create(this))
    {
        return -1;
    }


    m_appButton.SetImage(IDB_APP_BUTTON);


    m_ribbon.SetApplicationButton(&m_appButton,
        CSize(45, 45));


    CMFCRibbonMainPanel* appButtonMenu =
        m_ribbon.AddMainCategory(L"Menu",
        IDB_APP_BUTTON_MENU_SMALL,
        IDB_APP_BUTTON_MENU_LARGE);


    appButtonMenu->Add(new CMFCRibbonButton(ID_FILE_NEW,
        L"&New",
        0, // small image index
        0)); // large image index


    appButtonMenu->Add(new CMFCRibbonButton(ID_FILE_OPEN,
        L"&Open...",
        1, // small image index
        1)); // large image index


    appButtonMenu->AddToBottom(new CMFCRibbonMainPanelButton(ID_APP_EXIT,
        L"E&xit",
        15));
        //small image index


    CMFCRibbonCategory* category = m_ribbon.AddCategory(L"Home",
        IDB_RIBBON_CAT_HOME_SMALL,
        IDB_RIBBON_CAT_HOME_LARGE);


    CMFCRibbonPanel* panel = category->AddPanel(L"Clipboard");


    panel->Add(new CMFCRibbonButton(ID_EDIT_PASTE,
        L"Paste",
        0, // small image index
        0)); // large image index


    panel->Add(new CMFCRibbonButton(ID_EDIT_CUT, L"Cut", 1));
    panel->Add(new CMFCRibbonButton(ID_EDIT_COPY, L"Copy", 2));
    panel->Add(new CMFCRibbonButton(ID_EDIT_SELECT_ALL,
        L"Select All", -1));


    m_ribbon.AddCategory(L"Insert",
        IDB_RIBBON_CAT_HOME_SMALL,
        IDB_RIBBON_CAT_HOME_LARGE);


    CMFCVisualManager::SetDefaultManager(
        RUNTIME_CLASS(CMFCVisualManagerOffice
        2007));
    CMFCVisualManagerOffice2007::SetStyle
        (CMFCVisualManagerOffice2007::Office
        2007_LunaBlue);


    return 0;
}


从概念上讲,功能区由多个被称为类别的选项卡组成,每个选项卡都承载着一组面板。反过来,这些面板又承载着功能区元素或控件,它们分别代表特定于应用程序的各种操作。如果功能区承载着应用程序按钮(左上角的大圆按钮),则在用户单击应用程序按钮时所显示的弹出窗口中也会显示一个面板,它被视为功能区的主类别。
CMFCRibbonBar 类可实现功能区栏本身,而 CMFCRibbonApplicationButton 类则代表功能区栏所承载且显示在窗口框架左上角的应用程序按钮。功能区栏通常是在 WM_CREATE 消息处理程序中创建和准备的。要创建功能区栏,只需调用 CMFCRibbonBar 的 Create 成员函数,以提供将其附加到其中的窗口框架的地址即可。然后根据需要填充它。AddMainCategory 成员函数将主类别添加到功能区,并返回一个指向 CMFCRibbonMainPanel(您可以向其中添加将要显示在此面板中的功能区元素)的指针。
通过调用 AddCategory 成员函数,可向其中添加更多的类别来表示功能区的选项卡。AddCategory 返回一个指向 CMFCRibbonCategory 对象(您可使用其 AddPanel 成员函数向其中添加面板)的指针。AddPanel 返回一个指向 CMFCRibbonPanel 对象(可像使用功能区的主面板一样向其中添加功能区元素)的指针。最后,您可使用 CMFCVisualManager::SetDefaultManager 静态成员函数来设置负责处理框架窗口的样式和外观的可视化管理器。图 3 显示了功能区应用程序的可能外观(假定您已为功能区栏上的按钮添加了必要的事件处理程序)。


图 3 功能区示例应用程序


选项卡式多文档界面
MFC 一直都支持 MDI 实现及其文档/视图体系结构,但图 4 中所示的传统 MDI 早已过时,用户可能会认为您的应用程序从 Windows? 95 开始就再也没有更新过。现在,绝大多数用户都期望可通过窗口边缘的选项卡来访问多个文档,而这正是新的 CMDIFrameWndEx MDI 框架窗口所提供的功能。


图 4 石器时代的窗口 (单击该图像获得较大视图)
您需要更新多文档/视图应用程序对象以支持新的框架窗口。图 5 显示了满足需要的最小应用程序对象。它类似于传统的 MDI 应用程序对象,但有几点值得一提。
   Figure 5 选项卡式 MDI 应用程序对象
class Application : public CWinAppEx
{
    DECLARE_MESSAGE_MAP()


public:


    virtual BOOL InitInstance();


};


BEGIN_MESSAGE_MAP(Application, CWinAppEx)
    ON_COMMAND(ID_FILE_NEW, &CWinAppEx::OnFileNew)
END_MESSAGE_MAP()


BOOL Application::InitInstance()
{
    SetRegistryKey(L"SampleCompany\\SampleProduct");


    VERIFY(InitContextMenuManager());


    AddDocTemplate(new CMultiDocTemplate(IDR_CHILDFRAME,
                                         RUNTIME_CLASS(Document),
                                         RUNTIME_CLASS(CMDIChildWndEx),
                                         RUNTIME_CLASS(View)));


    MainWindow* mainWindow = new MainWindow();
    VERIFY(mainWindow->LoadFrame(IDR_MAINFRAME));


    m_pMainWnd = mainWindow;
    m_pMainWnd->ShowWindow(m_nCmdShow);
    m_pMainWnd->UpdateWindow();


    return TRUE;
}


首先,子窗口框架的运行时类是 CMDIChildWndEx,而非传统的 CMDIChildWnd 类。要准备在选项卡式视图之间进行切换时使用的菜单管理器,还需调用 InitContextMenuManager 函数。
图 6 显示了最小 MDI 框架窗口。同样,您会非常高兴地看到开启此功能是多么地简单。实际只需调用 EnableMDITabbedGroups 成员函数来启用 MDI 选项卡式分组功能即可。CMDITabInfo 类提供了各种成员变量,可使用它们来自定义选项卡式分组的外观和行为。顾名思义,它甚至还允许用户拖动不同的视图来创建垂直或水平对齐的选项卡组。图 7 显示了其可能的外观。
   Figure 6 选项卡式 MDI 框架窗口
class MainWindow : public CMDIFrameWndEx
{
    DECLARE_DYNCREATE(MainWindow)
    DECLARE_MESSAGE_MAP()


private:


    int OnCreate(CREATESTRUCT* createStruct);


};


IMPLEMENT_DYNCREATE(MainWindow, CMDIFrameWndEx)


BEGIN_MESSAGE_MAP(MainWindow, CMDIFrameWndEx)
    ON_WM_CREATE()
END_MESSAGE_MAP()


int MainWindow::OnCreate(CREATESTRUCT* createStruct)
{
    if (-1 == __super::OnCreate(createStruct))
    {
        return -1;
    }


    CMDITabInfo tabInfo;
    tabInfo.m_bAutoColor = true;
    tabInfo.m_bDocumentMenu = true;
    EnableMDITabbedGroups(true, tabInfo);


    return 0;
}



图 7 现代选项卡式 MDI 应用程序 (单击该图像获得较大视图)


标准 C++ 库中的新功能
正如我所提到的,功能包还包括作为 TR1 的一部分添加到标准 C++ 库中的大量附加功能。其中包括支持引用计数的智能指针、多态函数包装、基于哈希表的容器、正则表达式等等。下面我将介绍其中的一些新 TR1 功能。


多态函数对象
在许多应用程序中都有一个至关重要的功能,就是能够将函数作为一个值加以引用并能够将其作为参数来传递或存储起来以备今后使用。此概念可用于实现各种常见的构造,包括回调函数、事件处理程序和异步编程功能等。但是,函数在 C++ 中非常难于处理。函数设计的驱动力主要源自与 C 的兼容性的要求以及对优良性能的要求。尽管实现了这些目标,但在将函数视为可存储、可传递并最终能够异步调用的对象方面,却并未能使其变得简单一些。让我们来看一看 C++ 中常见的一些类似函数的构造。
首先,是一个不错的古老非成员函数:
int Add(int x, int y)
{
    return x + y;
}
正常情况下,可通过如下方法调用它:
int result = Add(4, 5);
ASSERT(4 + 5 == result);
另一个常见的类似函数的构造是函数对象(即算符):
class AddFunctor
{
public:
    int operator()(int x, int y) const
    {
        return x + y;
    }
};
由于它实现调用运算符,因此可像使用函数一样来使用函数对象:
    AddFunctor fo;
  
   int result = fo(4, 5);
   ASSERT(4 + 5 == result);
接下来是非静态成员函数:
    class Adder
   {
   public:
       int Add(int x, int y) const
       {
           return x + y;
       }
   };
当然,调用成员函数需要使用对象:
Adder adder;


int result = adder.Add(4, 5);
ASSERT(4 + 5 == result);
到目前为止一切顺利。现在,假设您需要将这些类似函数的构造存储起来以备今后使用。可按如下方式定义一个能存储指向非成员函数的指针的类型:
typedef int (*FunctionPointerType)(int x, int y);
也可将函数指针作为函数来使用:
FunctionPointerType fp = &Add;


int result = fp(4, 5);
ASSERT(4 + 5 == result);
尽管函数对象也可以存储下来,但它无法与函数指针一起以多态形式存储。
成员函数可存储在 pointer-to-member-function 中:
Adder adder;


typedef int (Adder::*MemberFunctionPointerType)(int x, int y);
MemberFunctionPointerType mfp = &Adder::Add;
但是,pointer-to-member-function 类型与 pointer-to-non-member-function 类型不兼容,因此无法与其非成员函数竞争者一起以多态形式存储。即使可以,成员函数仍需要一个对象来提供成员函数调用的上下文:
int result = (adder.*mfp)(4, 5);
ASSERT(4 + 5 == result);
我想我不必再做解释您也应该明白我的意思了。幸运的是,新的 tr1::function 类模板提供了一个解决方案。tr1::function 类模板为在其模板参数中定义的函数类型保存着一个可调用对象。接下来,我将使用非成员函数对其进行初始化:
function<int (int x, int y)> f = &Add;


int result = f(4, 5);
ASSERT(4 + 5 == result);
使用函数对象来初始化也一样轻松:
function<int (int x, int y)> f = AddFunctor();
您甚至还可以使用新的函数绑定功能通过成员函数对其进行初始化:
function<int (int x, int y)> f = bind(&Adder::Add, &adder, _1, _2);
有关 bind 函数的内容我会在稍后做介绍,但在这里您需要了解的就是现在可将单个函数包装绑定到非成员函数、函数对象甚至成员函数中。可将其存储下来并在今后随时调用,所有这一切都是以多态形式执行的。
函数包装也是可以重新绑定的,并且可以像普通的函数指针一样设置为空值:
function<int (int x, int y)> f;
ASSERT(0 == f);


f = &Add;
ASSERT(0 != f);


f = bind(&Adder::Add, &adder, _1, _2);
bind 函数模板的功能要比标准 C++ 库中的函数对象适配器强大得多——尤其是 std::bind1st() 和 std::bind2nd()。在这个示例中,bind 的第一个参数是成员函数的地址。第二个参数是对象的地址,届时将在此对象中调用成员。此示例中的最后两个参数定义了调用函数时将要解析的占位符。
当然,bind 并不仅限于成员函数。您可通过绑定标准 C++ 库的 multiplies 函数对象来创建一个平方函数,利用此函数可生成一个能得出参数平方结果的单参数函数:
function<int (int)> square = bind(multiplies<int>(), _1, _1);


int result = square(3);
ASSERT(9 == result);
请注意,tr1::function 类模板非常适合与标准 C++ 库算法一起使用。给定一个整数容器,就可以使用成员函数生成所有值的总和,如下所示:
function<int (int x, int y)> f = // initialize


int result = accumulate(numbers.begin(),
                        numbers.end(),
                        0, // initial value
                        f);
请记住,tr1::function 类模板可能会禁止编译器优化(如内联),但如果您只是直接使用函数指针或函数对象则可能不会出现这一问题。因此,请仅在必要时才使用 tr1::function 类模板,例如当使用可能会被重复调用的累积算法时。如果可能,应直接将函数指针、成员函数指针(使用 TR1 的 mem_fn 改写过的)以及函数对象(如 bind 所返回的)传递给标准 C++ 库算法和其他模板化的算法。
让我们接着往下看。接下来还有个更有趣的问题。假设有个 Surface 类代表一些绘图表面,还有个 Shape 类,它可以将其自身绘制到表面上:
class Surface
{
    //...
};


class Shape
{
public:
    void Draw(Surface& surface) const;
};
现在考虑一下怎样才能够将容器中的每个形状都绘制到给定表面上。您可能会考虑使用 for_each 算法,如下所示:
Surface surface = // initialize


for_each(shapes.begin(),
         shapes.end(),
         bind(&Shape::Draw, _1, surface)); // wrong
在这里,我打算利用 bind 函数模板来针对形状容器的每个元素调用成员函数,从而将表面作为参数绑定到 Draw 成员函数。但遗憾的是,这要取决于 Surface 的定义方式,有时可能无法按预期的那样运行或编译。之所以出现这个问题,是因为当您实际需要的是一个引用时,bind 函数模板却试图生成表面副本。值得庆幸的是,TR1 还引入了 reference_wrapper 类模板,它允许您将引用视为一个可随意复制的值。由于类型推断功能的存在,ref 和 cref 函数模板可简化 reference_wrapper 对象的创建过程。
借助于 reference_wrapper,for_each 算法现在可以简单有效地将形状成功绘制到表面上:
for_each(shapes.begin(),
         shapes.end(),
         bind(&Shape::Draw, _1, ref(surface)));
正如您所设想的,对于新的函数包装、绑定功能和引用包装,可以通过多种方式来组合它们,从而灵活地解决各种问题。


智能指针
智能指针对 C++ 开发人员而言是不可或缺的工具。我通常使用 ATL 的 CComPtr 来处理 COM 接口指针,使用标准 C++ 库的 auto_ptr 来处理原始 C++ 指针。后者非常适合需要动态创建 C++ 对象并能够确保当 auto_ptr 对象超出范围时可以将对象安全删除的情形。
智能指针像 auto_ptr 一样都非常有用,但它只能安全地用在少数情形下。这主要是因为它所实现的所有权转移语义。即,如果复制或分配 auto_ptr 对象,则基础资源的所有权将被转移,原始 auto_ptr 对象将失去它。只有当您对资源分配拥有精细的控制权时它才会有明显作用,但很多情况下您可能需要共享对象,这时实现共享所有权语义的智能指针将会非常有用。更为重要的是,auto_ptr 无法与标准 C++ 库容器一起使用。
TR1 引入了两个新的智能指针,它们协同工作来提供多种用途。shared_ptr 类模板的工作方式与 auto_ptr 十分相似,但它不能转移资源的所有权,它只是增加资源的引用计数。如果用来保存对象引用信息的最后一个 shared_ptr 对象被破坏或重置,资源将被自动删除。通过 weak_ptr 类模板与 shared_ptr 的协同工作,调用方可以在不影响引用计数的情况下引用资源。如果在对象模型中有循环关系或打算实现缓存服务,则这将非常有用。它还非常适合与标准 C++ 库容器一起使用!
作为对比,请看一看以下的 auto_ptr 用法:
auto_ptr<int> ap(new int(123));


ASSERT(0 != ap.get());


// transfer ownership from ap to ap2
auto_ptr<int> ap2(ap);


ASSERT(0 != ap2.get());
ASSERT(0 == ap.get());
auto_ptr 复制构造函数将所有权从 ap 传输到 ap2。shared_ptr 的行为同样是可预测的:
shared_ptr<int> sp(new int(123));


ASSERT(0 != sp);


// increase reference count of shared object
shared_ptr<int> sp2(sp);


ASSERT(0 != sp2);
ASSERT(0 != sp);
从内部来说,引用相同资源的所有 shared_ptr 对象都共享一个控制块,此控制块将跟踪共同拥有资源的 shared_ptr 对象的数量以及引用此资源的 weak_ptr 对象的数量。稍后我将展示如何使用 weak_ptr 类模板。
与 auto_ptr 类似的成员函数由 shared_ptr 提供。其中包括解引用操作符和箭头操作符、用来替换资源的 reset 成员函数以及返回资源地址的 get 成员函数。此外还提供一些特有的成员函数(其中包括一个恰好也以 unique 命名的函数)。unique 成员函数将测试 shared_ptr 对象是否为保存着资源引用信息的唯一智能指针。示例如下:
shared_ptr<int> sp(new int(123));
ASSERT(sp.unique());


shared_ptr<int> sp2(sp);
ASSERT(!sp.unique());
ASSERT(!sp2.unique());
也可以使用 use_count 成员函数来获取拥有资源的 shared_ptr 对象的数量:
shared_ptr<int> sp;
ASSERT(0 == sp.use_count());


sp.reset(new int(123));
ASSERT(1 == sp.use_count());


shared_ptr<int> sp2(sp);
ASSERT(2 == sp.use_count());
ASSERT(2 == sp2.use_count());
但是,应将 use_count 的用途仅限于进行调试,因为无法保证它在所有实现中都是一个恒定的时间操作。请注意,可借助提供的操作符 unspecified-bool-type 来确定 shared_ptr 是否拥有资源,并且可使用 unique 函数来确定 shared_ptr 是否为某个资源的唯一拥有者。
weak_ptr 类模板存储着对 shared_ptr 对象所拥有的资源的弱引用。如果拥有资源的所有 shared_ptr 对象都被破坏或重置,资源将被删除,无论是否有 weak_ptr 对象正在引用它。为确保不使用仅被 weak_ptr 对象引用的资源,weak_ptr 类模板不会提供熟悉的 get 成员函数来返回资源的地址或成员访问操作符。相反,首先必须将弱引用转换为强引用才能访问资源。lock 成员函数提供了此功能,如图 8 所示。
   Figure 8 转换成强引用
Surface surface;


shared_ptr<Shape> sp(new Shape);
ASSERT(1 == sp.use_count());


weak_ptr<Shape> wp(sp);
ASSERT(1 == sp.use_count()); // still 1


// arbitrary application logic...


if (shared_ptr<Shape> sp2 = wp.lock())
{
    sp2->Draw(surface);
}


如果资源中途被释放,weak_ptr 对象的 lock 成员函数会返回一个并不拥有资源的 shared_ptr 对象。可以想象,shared_ptr 和 weak_ptr 必将会极大地简化许多应用程序中的资源管理工作。
毋庸置疑,Visual C++ 2008 功能包是一个备受欢迎的 Visual C++ 库升级程序,肯定迟早会派上用场!值得研究的内容不胜枚举,但我希望通过本文的介绍能激起您的兴趣,使您能够花些时间亲自深入研究一下。


Kenny Kerr 是一名专门从事 Windows 软件开发的软件专家。他热衷于撰写有关编程和软件设计的文章,并向开发人员讲授与此有关的知识。您可通过他的博客 weblogs.asp.net/kennykerr 与 Kenny 联系。


QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 4楼 发表于: 2011-12-18
大家可能一直在用VC开发软件,但是对于这个编译器却未必很了解。原因是多方面的。大多数情况下,我们只停留在“使用”它,而不会想去“了解”它。因为它只是一个工具,我们宁可把更多的精力放在C++语言和软件设计上。我们习惯于这样一种“模式”:建立一个项目,然后写代码,然后编译,反反复复调试。但是,所谓:“公欲善其事,必先利其器”。如果我们精于VC开发环境,我们是不是能够做得更加游刃有余呢?

闲话少说。我们先来看一下VC的处理流程,大致分为两步:编译和连接。源文件通过编译生成了.obj文件;所有.obj文件和.lib文件通过连接生成.exe文件或.dll文件。下面,我们分别讨论这两个步骤的一些细节。

编译参数的设置。主要通过VC的菜单项Project->Settings->C/C++页来完成。我们可以看到这一页的最下面Project Options中的内容,一般如下:

/nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /Fp"Debug/WritingDlgTest.pch" /Yu"stdafx.h" /Fo"Debug/" /Fd"Debug/" /FD /GZ /c

各个参数代表的意义,可以参考Msdn。比如/nologo表示编译时不在输出窗口显示这些设置(我们可以把这个参数去掉来看看效果)等等。一般我们不会直接修改这些设置,而是通过这一页最上面的Category中的各项来完成。

1) General:一些总体设置。Warning level用来控制警告信息,其中Level 1是最严重的级别;Warnings as errors将警告信息当作错误处理;Optimizations是代码优化,可以在Category的Optimizations项中进行更细的设置;Generate browse info用以生成.sbr文件,记录类、变量等符号信息,可以在Category的Listing Files项中进行更多的设置。Debug info,生成调试信息:None,不产生任何调试信息(编译比较快);Line Numbers Only,仅生成全局的和外部符号的调试信息到.OBJ文件或.EXE文件,减小目标文件的尺寸;C 7.0- Compatible,记录调试器用到的所有符号信息到.OBJ文件和.EXE文件;Program Database,创建.PDB文件记录所有调试信息;Program Database for "Edit & Continue",创建.PDB文件记录所有调试信息,并且支持调试时编辑。

2) C++ Language:pointer_to_member representation用来设置类定义/引用的先后关系,一般为Best-Case Always表示在引用类之前该类肯定已经定义了;Enable Exception Handling,进行同步的异常处理;Enable Run-Time Type Information迫使编译器增加代码在运行时进行对象类型检查;Disable Construction Displacements,设置类构造/析构函数调用虚函数问题。

3) Code Generation:Processor表示代码指令优化,可以为80386、80486、Pentium、Pentium Pro,或者Blend表示混合以上各种优化。Use run-time library用以指定程序运行时使用的运行时库(单线程或多线程,Debug版本或Release版本),有一个原则就是,一个进程不要同时使用几个版本的运行时库。Single-Threaded,静态连接LIBC.LIB库;Debug Single-Threaded,静态连接LIBCD.LIB库;Multithreaded,静态连接LIBCMT.LIB库;Debug Multithreaded,静态连接LIBCMTD.LIB库;Multithreaded DLL,动态连接MSVCRT.DLL库;Debug Multithreaded DLL,动态连接MSVCRTD.DLL库。连接了单线程库就不支持多线程调用,连接了多线程库就要求创建多线程的应用程序。Calling convention可以用来设定调用约定,有三种:__cdecl、__fastcall和__stdcall。各种调用约定的主要区别在于,函数调用时,函数的参数是从左到右压入堆栈还是从右到左压入堆栈;在函数返回时,由函数的调用者来清理压入堆栈的参数还是由函数本身来清理;以及在编译时对函数名进行的命名修饰(可以通过Listing Files看到各种命名修饰方式)。Struct member alignment用以指定数据结构中的成员变量在内存中是按几字节对齐的,根据计算机数据总线的位数,不同的对齐方式存取数据的速度不一样。这个参数对数据包网络传输等应用尤为重要,不是存取速度问题,而是数据位的精确定义问题,一般在程序中使用#pragma pack来指定。

4) Customize:Disable Language Extensions,表示不使用微软为标准C做的语言扩展;Eliminate Duplicate Strings,主要用于字符串优化(将字符串放到缓充池里以节省空间),使用这个参数,使得

char *sBuffer = "This is a character buffer";

char *tBuffer = "This is a character buffer";

sBuffer和tBuffer指向的是同一块内存空间;Enable Function-Level Linking ,告诉编译器将各个函数按打包格式编译;Enables minimal rebuild,通过保存关联信息到.IDB文件,使编译器只对最新类定义改动过的源文件进行重编译,提高编译速度;Enable Incremental Compilation,同样通过.IDB文件保存的信息,只重编译最新改动过的函数;Suppress Startup Banner and Information Messages,用以控制参数是否在output窗口输出。

5) Listing Files:Generate browse info的功能上面已经提到过。这里可以进行更多的设置。Exclude Local Variables from Browse Info表示是否将局部变量的信息放到.SBR文件中。Listing file type可以设置生成的列表信息文件的内容:Assembly-Only Listing仅生成汇编代码文件(.ASM扩展名);Assembly With Machine Code生成机器代码和汇编代码文件(.COD扩展名);Assembly With Source Code生成源代码和汇编代码文件(.ASM扩展名);Assembly, Machine Code, and Source生成机器码、源代码和汇编代码文件(.COD扩展名)。Listing file name为生成的信息文件的路径,一般为Debug或Release目录下,生成的文件名自动取源文件的文件名。

6) Optimizations:代码优化设置。可以选择Maximize Speed生成最快速的代码,或Minimize Size生成最小尺寸的程序,或者Customize定制优化。定制的内容包括:

Assume No Aliasing,不使用别名(提高速度);

Assume Aliasing Across Function Calls,仅函数内部不使用别名;

Global Optimizations,全局优化,比如经常用到的变量使用寄存器保存,或者循环内的计算优化,如

i = -100;

while( i < 0 ){ i += x + y;}
会被优化为
i = -100;
t = x + y;
while( i < 0 ){i += t;}
Generate Intrinsic Functions,使用内部函数替换一些函数调用(提高速度);
Improve Float Consistency,浮点运算方面的优化;
Favor Small Code,程序(exe或dll)尺寸优化优先于代码速度优化;
Favor Fast Code,程序(exe或dll)代码速度优化优先于尺寸优化;
Frame-Pointer Omission,不使用帧指针,以提高函数调用速度;
Full Optimization,组合了几种参数,以生成最快的程序代码。
Inline function expansion,内联函数扩展的三种优化(使用内联可以节省函数调用的开销,加快程序速度):Disable不使用内联;Only __inline,仅函数定义前有inline或__inline标记使用内联;Any Suitable,除了inline或__inline标记的函数外,编译器“觉得”应该使用内联的函数,都使用内联。
7) Precompiled Headers:预编译头文件的设置。使用预编译可以提高重复编译的速度。VC一般将一些公共的、不大变动的头文件(比如afxwin.h等)集中放到stdafx.h中,这一部分代码就不必每次都重新编译(除非是Rebuild All)。

8) Preprocessor:预编译处理。可以定义/解除定义一些常量。Additional include directories,可以指定额外的包含目录,一般是相对于本项目的目录,如..\Include。

连接参数的设置。主要通过VC的菜单项Project->Settings->Link页来完成。我们可以看到这一页的最下面Project Options中的内容,一般如下:

/nologo /subsystem:windows /incremental:yes /pdb:"Debug/WritingDlgTest.pdb" /debug /machine:I386 /out:"Debug/WritingDlgTest.exe" /pdbtype:sept

下面我们分别来看一下Category中的各项设置。

1) General:一些总体设置。可以设置生成的文件路径、文件名;连接的库文件;Generate debug info,生成Debug信息到.PDB文件(具体格式可以在Category->Debug中设置);Ignore All Default Libraries,放弃所有默认的库连接;Link Incrementally,通过生成. ILK文件实现递增式连接以提高后续连接速度,但一般这种方式下生成的文件(EXE或DLL)较大;Generate Mapfile,生成.MAP文件记录模块相关信息;Enable Profiling,这个参数通常与Generate Mapfile参数同时使用,而且如果产生Debug信息的话,不能用.PDB文件,而且必须用Microsoft Format。

2) Customize:这里可以进行使用程序数据库文件的设置。Force File Output ,强制产生输出文件(EXE或DLL);Print Progress Messages,可以将连接过程中的进度信息输出到Output窗口。

3) Debug:设置是否生成调试信息,以及调试信息的格式。格式可以有Microsoft Format、COFF Format(Common Object File Format)和Both Formats三种选择;Separate Types,表示将Debug格式信息以独立的.PDB文件存放,还是直接放在各个源文件的.PDB文件中。选中的话,表示采用后者的方式,这种方式调试启动比较快。

4) Input:这里可以指定要连接的库文件,放弃连接的库文件。还可以增加额外的库文件目录,一般是相对于本项目的目录,如..\Lib。Force Symbol References,可以指定连接特定符号定义的库。

5) Output:Base Address可以改变程序默认的基地址(EXE文件默认为0x400000,DLL默认为0x10000000),操作系统装载一个程序时总是试着先从这个基地址开始。Entry-Point Symbol可以指定程序的入口地址,一般为一个函数名(且必须采用__stdcall调用约定)。一般Win32的程序,EXE的入口为WinMain,DLL的入口为DllEntryPoint;最好让连接器自动设置程序的入口点。默认情况下,通过一个C的运行时库函数来实现:控制台程序采用mainCRTStartup (或wmainCRTStartup)去调用程序的main (或wmain)函数;Windows程序采用WinMainCRTStartup (或 wWinMainCRTStartup)调用程序的WinMain (或 wWinMain,必须采用__stdcall调用约定);DLL采用_DllMainCRTStartup调用DllMain函数(必须采用__stdcall调用约定)。Stack allocations,用以设置程序使用的堆栈大小(请使用十进制),默认为1兆字节。Version Information告诉连接器在EXE或DLL文件的开始部分放上版本号。

值得注意的是,上面各个参数是大小写敏感的;在参数后加上“-”表示该参数无效;各个参数值选项有“*”的表示为该参数的默认值;可以使用页右上角的“Reset”按钮来恢复该页的所有默认设置。


其它一些参数设置。

1) Project->Settings->General,可以设置连接MFC库的方式(静态或动态)。如果是动态连接,在你的软件发布时不要忘了带上MFC的DLL。

2) Project->Settings->Debug,可以设置调试时运行的可执行文件,以及命令行参数等。

3) Project->Settings->Custom Build,可以设置编译/连接成功后自动执行一些操作。比较有用的是,写COM时希望VC对编译通过的COM文件自动注册,可以如下设置:

Description: Register COM

Commands: regsvr32 /s /c $(TargetPath)

echo regsvr32 exe.time > $(TargetDir)\$(TargetName).trg

Outputs: $(TargetDir)\$(TargetName).trg

4) Tools->Options->Directories,设置系统的Include、Library路径。


一些小窍门

1) 有时候,你可能在编译的时候,计算机突然非法关机了(可能某人不小心碰了电源或你的内存不稳定等原因)。当你重启机器后打开刚才的项目,重新进行编译,发现VC会崩掉。你或许以为你的VC编译器坏了,其实不然(你试试编译其它项目,还是好的!),你只要将项目的.ncb、.opt、.aps、.clw文件以及Debug、Release目录下的所有文件都删掉,然后重新编译就行了。

2) 如果你想与别人共享你的源代码项目,但是把整个项目做拷贝又太大。你完全可以删掉以下文件:.dsw、.ncb、.opt、.aps、.clw、. plg文件以及Debug、Release目录下的所有文件。

3) 当你的Workspace中包含多个Project的时候,你可能不能直观地、一眼看出来哪个是当前项目。可以如下设置:Tools->Options->Format,然后在Category中选择Workspace window,改变其默认的字体(比如设成Fixedsys)就行了。

4) 如何给已有的Project改名字?将该Project关掉。然后以文本格式打开.dsp文件,替换原来的Project名字即可。

5) VC6对类成员的智能提示功能很有用,但有时候会失灵。你可以先关掉项目,将.clw和.ncb删掉,然后重新打开项目,点击菜单项View->ClassWizard,在弹出的对话框中按一下“Add All”按钮;重新Rebuild All。应该可以解决问题
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 5楼 发表于: 2012-02-26
VS2008/2010 检查内存泄露的方法
1. 在APP开头处包含以下代码
#define CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

2. 在APP初始化的函数中加入
_CrtDumpMemoryLeaks();

3. Output中将会跟踪所有内存创建和销毁的过程,这些信息可以忽略。

4. 程序退出时,output中将会显示出创建内存未释放的代码行信息。
这个可以解决绝大部分情况下出现的内存泄露
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 6楼 发表于: 2012-02-29
VS2010中MSDN安装方法
VS2010正式版不再有单独的MSDN Library安装选项,以至于很多同学找不到本地的MSDN Library来用,其实VS2010的ISO安装光盘里已经包含有MSDN     Library,只不过要手动安装,方法如下:
1、安装完VS2010后,在开始菜单中打开Microsoft Visual Studio 2010 - Visual Studio Tools – Manage Help Settings,第一次打开时会让你选择一个路径用于保存MSDN Library的内容,建议选择一个剩余空间比较大的盘
2、点击“Choose online or local help”,然后选“I want to use local help”
3、点击“Install content from disk”,然后选择VS2010安装光盘下的ProductDocumentation\HelpContentSetup.msha文件
4、点击“Add”选择你要安装的MSDN Library内容,然后点OK就开始安装了
5、(可选)点击“Check for updates online”可以在线更新本地MSDN Library文档为最新的内容。
VS2010中不在叫MSDN Library,而叫Help Library,在VS2010里按F1即可打开Help Library,第一次打开时会额外运行一个Help Library Agent进程,然后调用默认浏览器打开Help Library。
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 7楼 发表于: 2012-04-09
VC2008中影响exe大小和速度的全部优化选项
VC就得用IDE,我也以IDE的工程设置里面的排列顺序介绍,某些选项需要自己手动添加的最后介绍,我后面说的默认值是release的,debug版本一般不需要调选项。

项目 - 属性 - 配置属性 - C/C++,这是编译器选项。

优化:
    
通常,算法程序选择最大化速度(/O2),界面程序选择最小化大小(/O1),可以获得最佳的效果。
    优选大小或速度,只有在使用完全优化(/Ox)时才有效,完全优化一般不推荐使用,用处就是可以生成速度与/O2基本相当,但是体积更小的代码(选速度优先的话)。

    
其他几个选项实际上已包含在/O1/O2之中,具体请看MSDN

代码生成:
    
启用字符串池(/GF),会将相同的字符串合并,当然可以减小空间占用,虽然本项目默认没有打开,但是默认的/Zi选项会自动打开/GF,这里打不打开一样。
    
启用C++异常:该项默认打开,在C++项目中(比如MFC中),会大大增加程序体积,增加约30%,关闭并不代表try不能用了,但会一定程度上降低健壮性,对于空间要求较高的程序,建议关闭。对于正式项目,请参见MSDN,看看会不会造成不利影响。
    
运行库:默认多线程DLL(/MD)体积最优的方案,如果对方没有VS运行时库,选择/MT会将C/C++运行库静态编译,体积增加不少,因此,我的选择一般是程序与redist包一起发布,也就几M,而且以后永远可以接受/MD版本了。
    
缓冲区安全检查:关闭的话,减少0.5K~1K体积(默认情况,VC的段长度512字节,因此程序体积变化的最小单位是0.5K)。
    
启用增强指令集:真想用SSE3的话去用Intel C++VS2008只支持到SSE2,而且,在我的机器上貌似使用默认设置就能达到选择SSE2的相同速度,如果安装了Intel C++ 11,可集成与VS2008,同样的地方选择SSE3效果超群
    
浮点模型:精确还是快速理论上肯定对速度有影响,但是我极少使用浮点编程,我的方向是系统、安全和密码,都是整数的天下。

高级:
    
编译为C还是C++影响不大,这充分说明了C++简单面向对象特性和C效率差不多(如重载,默认情况下,编译器会检查扩展名决定目标代码类型,对于cpp文件,所有的函数都会编译为可重载的类型,但是对效率几乎没有影响)。

项目 - 属性 - 配置属性 - 链接器,这是链接器选项。

输入:
    
忽略库只有在库冲突时候才有用,VC绝对不会连接没有调用到的库,哪怕你明确指定了。

清单文件:
    
完全使用API编程可以不生成清单。减少约1K体积。
    
一般情况下,关闭UAC的那一项,可减少0.5K

调试:
    
关闭生成调试信息(/DEBUG)”根据程序规模,可减少1K~几十K

优化:
    release
模式,默认情况下已经该组已经最优了,/OPT:REF/OPT:ICF已经打开,注意,VS2005VS2008Windows 98优化那一项没用,不像VC6取消Windows 98优化可以大大减小体积。因为VS2005VS2008中段大小已经是512字节,VC6默认4K

高级:
    
指定入口点,可以大大减小程序体积,但是不调用CRT的入口无法自动处理参数,可用GetCommandLineCommandLineToArgvW这两个API来处理参数。
    
随机基址:默认模式启用映像随机化(/DYNAMICBASE),会大大增加程序体积,因为这是个增加程序防反编译、防破解能力的选项。如无需求,请选择禁用映像随机化(/DYNAMICBASE:NO)文件越大,体积缩小越明显,至少30%

命令行:
    
小程序,可以指定段大小/ALIGN/O1编译的化最小可以使用/ALIGN:4,这个选项不推荐,第一有点规模的程序就不能用太小的段,/O2优化的也不能用小段,而且默认的512字节段可以使用UPX压缩,再小就不能了,除非咱们编译那种600字节的Hello World,这个选项意义不大,因此微软才没有给他一个图形选项。
    
同样,编译600字节hello world还需要/merge合并段选项,同样不推荐使用。

    
有些选项VS2005VS2003没有,VS2003还包括几个VS2008废除的选项,实际上VC里面程序优化效率最高的个人感觉是VS2003VC6的界面差别比较大,选项有一定差异,但毕竟都是微软的产品,差别不大,甚至于MASM这个汇编编译器,连接选项大都与VC相同……

    
再说一点,VS2008SP1MFC工程会自动生成巨大的256*256真彩图标,因此默认的MFC对话框程序都有近100K,建议删除多余的图标,配合上述选项能减到10K
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 8楼 发表于: 2012-04-09
VC编译器简介
大家可能一直在用VC开发软件,但是对于这个编译器却未必很了解。原因是多方面的。大多数情况下,我们只停留在“使用”它,而不会想去“了解”它。因为它只是一个工具,我们宁可把更多的精力放在C++语言和软件设计上。我们习惯于这样一种“模式”:建立一个项目,然后写代码,然后编译,反反复复调试。但是,所谓:“公欲善其事,必先利其器”。如果我们精于VC开发环境,我们是不是能够做得更加游刃有余呢?



闲话少说。我们先来看一下VC的处理流程,大致分为两步:编译和连接。源文件通过编译生成了.obj文件;所有.obj文件和.lib文件通过连接生成.exe文件或.dll文件。下面,我们分别讨论这两个步骤的一些细节。
编译参数的设置。主要通过VC的菜单项Project->Settings->C/C++页来完成。我们可以看到这一页的最下面Project Options中的内容,一般如下:
/nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_M
BCS" /Fp"Debug/WritingDlgTest.pch" /Yu"stdafx.h" /Fo"Debug/" /Fd"Debug/" /FD /GZ /c
各个参数代表的意义,可以参考Msdn。比如/nologo表示编译时不在输出窗口显示这些设置(我们可以把这个参数去掉来看看效果)等等。一般我们不会直接修改这些设置,而是通过这一页最上面的Category中的各项来完成。
1) General:一些总体设置。Warning level用来控制警告信息,其中Level 1是最严重的级别;Warnings as errors将警告信息当作错误处理;Optimizations是代码优化,可以在Category的Optimizations项中进行更细的设置;Generate browse info用以生成.sbr文件,记录类、变量等符号信息,可以在Category的Listing Files项中进行更多的设置。Debug info,生成调试信息:None,不产生任何调试信息(编译比较快);Line Numbers Only,仅生成全局的和外部符号的调试信息到.OBJ文件或.EXE文件,减小目标文件的尺寸;C 7.0- Compatible,记录调试器用到的所有符号信息到.OBJ文件和.EXE文件;Program Database,创建.PDB文件记录所有调试信息;Program Database for "Edit & Continue",创建.PDB文件记录所有调试信息,并且支持调试时编辑。
2) C++ Language:pointer_to_member representation用来设置类定义/引用的先后关系,一般为Best-Case Always表示在引用类之前该类肯定已经定义了;Enable Exception Handling,进行同步的异常处理;Enable Run-Time Type Information迫使编译器增加代码在运行时进行对象类型检查;Disable Construction Displacements,设置类构造/析构函数调用虚函数问题。
3) Code Generation:Processor表示代码指令优化,可以为80386、80486、Pentium、Pentium Pro,或者Blend表示混合以上各种优化。Use run-time library用以指定程序运行时使用的运行时库(单线程或多线程,Debug版本或Release版本),有一个原则就是,一个进程不要同时使用几个版本的运行时库。Single-Threaded,静态连接LIBC.LIB库;Debug Single-Threaded,静态连接LIBCD.LIB库;Multithreaded,静态连接LIBCMT.LIB库;Debug Multithreaded,静态连接LIBCMTD.LIB库;Multithreaded DLL,动态连接MSVCRT.DLL库;Debug Multithreaded DLL,动态连接MSVCRTD.DLL库。连接了单线程库就不支持多线程调用,连接了多线程库就要求创建多线程的应用程序。
Calling convention可以用来设定调用约定,有三种:__cdecl、__fastcall和__stdcall。各种调用约定的主要区别在于,函数调用时,函数的参数是从左到右压入堆栈还是从右到左压入堆栈;在函数返回时,由函数的调用者来清理压入堆栈的参数还是由函数本身来清理;以及在编译时对函数名进行的命名修饰(可以通过Listing Files看到各种命名修饰方式)。Struct member alignment用以指定数据结构中的成员变量在内存中是按几字节对齐的,根据计算机数据总线的位数,不同的对齐方式存取数据的速度不一样。这个参数对数据包网络传输等应用尤为重要,不是存取速度问题,而是数据位的精确定义问题,一般在程序中使用#pragma pack来指定。
4) Customize:Disable Language Extensions,表示不使用微软为标准C做的语言扩展;Eliminate Duplicate Strings,主要用于字符串优化(将字符串放到缓充池里以节省空间),使用这个参数,使得
char *sBuffer = "This is a character buffer";
char *tBuffer = "This is a character buffer";
sBuffer和tBuffer指向的是同一块内存空间;Enable Function-Level Linking ,告诉编译器将各个函数按打包格式编译;Enables minimal rebuild,通过保存关联信息到.IDB文件,使编译器只对最新类定义改动过的源文件进行重编译,提高编译速度;Enable Incremental Compilation,同样通过.IDB文件保存的信息,只重编译最新改动过的函数;Suppress Startup Banner and Information Messages,用以控制参数是否在output窗口输出。
5) Listing Files:Generate browse info的功能上面已经提到过。这里可以进行更多的设置。Exclude Local Variables from Browse Info表示是否将局部变量的信息放到.SBR文件中。Listing file type可以设置生成的列表信息文件的内容:Assembly-Only Listing仅生成汇编代码文件(.ASM扩展名);Assembly With Machine Code生成机器代码和汇编代码文件(.COD扩展名);Assembly With Source Code生成源代码和汇编代码文件(.ASM扩展名);Assembly, Machine Code,and Source生成机器码、源代码和汇编代码文件(.COD扩展名)。Listing file name为生成的信息文件的路径,一般为Debug或Release目录下,生成的文件名自动取源文件的文件名。
6) Optimizations:代码优化设置。可以选择Maximize Speed生成最快速的代码,或Minimize Size生成最小尺寸的程序,或者Customize定制优化。定制的内容包括:
Assume No Aliasing,不使用别名(提高速度);
Assume Aliasing Across Function Calls,仅函数内部不使用别名;
Global Optimizations,全局优化,比如经常用到的变量使用寄存器保存,或者循环内的计算优化,如
i = -100;
while( i < 0 ){ i += x + y;}
会被优化为
i = -100;
t = x + y;
while( i < 0 ){i += t;}
Generate Intrinsic Functions,使用内部函数替换一些函数调用(提高速度);
Improve Float Consistency,浮点运算方面的优化;
Favor Small Code,程序(exe或dll)尺寸优化优先于代码速度优化;
Favor Fast Code,程序(exe或dll)代码速度优化优先于尺寸优化;
Frame-Pointer Omission,不使用帧指针,以提高函数调用速度;
Full Optimization,组合了几种参数,以生成最快的程序代码。
Inline function expansion,内联函数扩展的三种优化(使用内联可以节省函数调用的开销,加快程序速度):Disable不使用内联;Only __inline,仅函数定义前有inline或__inline标记使用内联;Any Suitable,除了inline或__inline标记的函数外,编译器“觉得”应该使用内联的函数,都使用内联。
7) Precompiled Headers:预编译头文件的设置。使用预编译可以提高重复编译的速度。VC一般将一些公共的、不大变动的头文件(比如afxwin.h等)集中放到stdafx.h中,这一部分代码就不必每次都重新编译(除非是Rebuild All)。
8) Preprocessor:预编译处理。可以定义/解除定义一些常量。Additional include directories,可以指定额外的包含目录,一般是相对于本项目的目录,如..\Include。
连接参数的设置。主要通过VC的菜单项Project->Settings->Link页来完成。我们可以看到这一页的最下面Project Options中的内容,一般如下:
/nologo /subsystem:windows /incremental:yes /pdb:"Debug/WritingDlgTest.pdb" /debug /machi
ne:I386 /out:"Debug/WritingDlgTest.exe" /pdbtype:sept
下面我们分别来看一下Category中的各项设置。
1) General:一些总体设置。可以设置生成的文件路径、文件名;连接的库文件;Generate debug info,生成Debug信息到.PDB文件(具体格式可以在Category->Debug中设置);Ignore All Default Libraries,放弃所有默认的库连接;Link Incrementally,通过生成. ILK文件实现递增式连接以提高后续连接速度,但一般这种方式下生成的文件(EXE或DLL)较大;Generate Mapfile,生成.MAP文件记录模块相关信息;Enable Profiling,这个参数通常与Generate Mapfile参数同时使用,而且如果产生Debug信息的话,不能用.PDB文件,而且必须用Microsoft Format。
2) Customize:这里可以进行使用程序数据库文件的设置。Force File Output ,强制产生输出文件(EXE或DLL);Print Progress Messages,可以将连接过程中的进度信息输出到Output窗口。
3) Debug:设置是否生成调试信息,以及调试信息的格式。格式可以有Microsoft Format、COFF Format(Common Object File Format)和Both Formats三种选择;Separate Types,表示将Debug格式信息以独立的.PDB文件存放,还是直接放在各个源文件的.PDB文件中。选中的话,表示采用后者的方式,这种方式调试启动比较快。
4) Input:这里可以指定要连接的库文件,放弃连接的库文件。还可以增加额外的库文件目录,一般是相对于本项目的目录,如..\Lib。Force Symbol References,可以指定连接特定符号定义的库。
5) Output:Base Address可以改变程序默认的基地址(EXE文件默认为0x400000,DLL默认为x10000000),操作系统装载一个程序时总是试着先从这个基地址开始。Entry-Point Symbol可以指定程序的入口地址,一般为一个函数名(且必须采用__stdcall调用约定)。一般Win32的程序,EXE的入口为WinMain,DLL的入口为DllEntryPoint;最好让连接器自动设置程序的入口点。默认情况下,通过一个C的运行时库函数来实现:控制台程序采用mainCRTStartup (或wmainCRTStartup)去调用程序的main (或wmain)函数;Windows程序采用WinMainCRTStartup (或 wWinMainCRTStartup)调用程序的WinMain (或 wWinMain,必须采用__stdcall调用约定);DLL采用_DllMainCRTStartup调用DllMain函数(必须采用__stdcall调用约定)。Stack allocations,用以设置程序使用的堆栈大小(请使用十进制),默认为1兆字节。Version Information告诉连接器在EXE或DLL文件的开始部分放上版本号。
值得注意的是,上面各个参数是大小写敏感的;在参数后加上“-”表示该参数无效;各个参数值选项
有“*”的表示为该参数的默认值;可以使用页右上角的“Reset”按钮来恢复该页的所有默认设置。
其它一些参数设置
1) Project->Settings->General,可以设置连接MFC库的方式(静态或动态)。如果是动态连
接,在你的软件发布时不要忘了带上MFC的DLL。
2) Project->Settings->Debug,可以设置调试时运行的可执行文件,以及命令行参数等。
3) Project->Settings->Custom Build,可以设置编译/连接成功后自动执行一些操作。比较有
用的是,写COM时希望VC对编译通过的COM文件自动注册,可以如下设置:
Description: Register COM
Commands: regsvr32 /s /c $(TargetPath)
echo regsvr32 exe.time > $(TargetDir)\$(TargetName).trg
Outputs: $(TargetDir)\$(TargetName).trg
4) Tools->Options->Directories,设置系统的Include、Library路径。
一些小窍门
1) 有时候,你可能在编译的时候,计算机突然非法关机了(可能某人不小心碰了电源或你的内存不稳定等原因)。当你重启机器后打开刚才的项目,重新进行编译,发现VC会崩掉。你或许以为你的VC编译器坏了,其实不然(你试试编译其它项目,还是好的!),你只要将项目的.ncb、.opt、.aps、.clw文件以及Debug、Release目录下的所有文件都删掉,然后重新编译就行
了。
2) 如果你想与别人共享你的源代码项目,但是把整个项目做拷贝又太大。你完全可以删掉以下文件:.dsw、.ncb、.opt、.aps、.clw、. plg文件以及Debug、Release目录下的所有文件。
3) 当你的Workspace中包含多个Project的时候,你可能不能直观地、一眼看出来哪个是当前项目。可以如下设置:Tools->Options->Format,然后在Category中选择Workspace window,改变其默认的字体(比如设成Fixedsys)就行了。
4) 如何给已有的Project改名字?将该Project关掉。然后以文本格式打开.dsp文件,替换原来的Project名字即可。
5) VC6对类成员的智能提示功能很有用,但有时候会失灵。你可以先关掉项目,将.clw和.ncb删掉,然后重新打开项目,点击菜单项View->ClassWizard,在弹出的对话框中按一下“Add All”按钮;重新Rebuild All。应该可以解决问题。
参考资料:http://www.vcgood.com/forum_posts.asp?TID=1035

3月17日
C++编译器选项(VS2008)                         C/C++ 编译器选项
                              -优化-
/O1 最小化空间                          /O2 最大化速度
/Ob<n> 内联扩展(默认 n=0)               /Od 禁用优化(默认)
/Og 启用全局优化                        /Oi[-] 启用内部函数
/Os 优选代码空间                        /Ot 优选代码速度
/Ox 最大化优化                          /Oy[-] 启用帧指针省略

                             -代码生成-
/GF 启用只读字符串池                    /Gm[-] 启用最小重新生成
/Gy[-] 分隔链接器函数                   /GS[-] 启用安全检查
/GR[-] 启用 C++ RTTI                    /GX[-] 启用 C++ EH (与 /EHsc 相同)
/EHs 启用 C++ EH (没有 SEH 异常)        /EHa 启用 C++ EH (w/ SEH 异常)
/EHc 外部“C”默认为 nothrow            
/fp:<except[-]|fast|precise|strict> 选择浮点模式:
    except[-] - 在生成代码时考虑浮点异常
    fast -“fast”浮点模式;结果可预测性比较低
    precise -“precise”浮点模式;结果可预测
    strict -“strict” 浮点模式(意味着 /fp:except)
即使使用 /fp:except,/Qfast_transcendentals 也生成内联内部 FP
/GL[-] 启用链接时代码生成               /GA 为 Windows 应用程序进行优化
/Ge 对所有函数强制堆栈检查              /Gs[num] 控制堆栈检查调用
/Gh 启用 _penter 函数调用               /GH 启用 _pexit 函数调用
/GT 生成纤程安全 TLS 访问               /RTC1 启用快速检查(/RTCsu)
/RTCc 转换为较小的类型检查              /RTCs 堆栈帧运行时检查
/RTCu 未初始化的局部用法检查            
/clr[:option] 为公共语言运行库编译,其中 option 是:
    pure - 生成只包含 IL 的输出文件(没有本机可执行代码)
    safe - 生成只包含 IL 的可验证输出文件
    oldSyntax - 接受 Visual C++ 2002/2003 的托管扩展语法
    initialAppDomain - 启用 Visual C++ 2002 的初始 AppDomain 行为
    noAssembly - 不产生程序集           /Gd __cdecl 调用约定
/Gr __fastcall 调用约定                 /Gz __stdcall 调用约定
/GZ 启用堆栈检查(/RTCs)                 /QIfist[-] 使用 FIST 而不是 ftol()
/hotpatch 确保可热修补映像的函数填充    
/arch:<SSE|SSE2> CPU 结构的最低要求,为以下内容之一:
    SSE - 启用支持 SSE 的 CPU 可用的指令
    SSE2 - 启用支持 SSE2 的 CPU 可用的指令
/Qimprecise_fwaits 仅在“try”边界上生成 FWAITs,而不是“try”内部

                              -输出文件-
/Fa[file] 命名程序集列表文件            /FA[scu] 配置程序集列表
/Fd[file] 命名 .PDB 文件                /Fe<file> 命名可执行文件
/Fm[file] 命名映射文件                  /Fo<file> 命名对象文件
/Fp<file> 命名预编译头文件              /Fr[file] 命名源浏览器文件
/FR[file] 命名扩展 .SBR 文件            
/doc[file] 处理 XML 文档注释,并可选择命名 .xdc 文件

                              -预处理器-
/AI<dir> 添加到程序集搜索路径           /FU<file> 强制使用程序集/模块
/C 不抽出注释                           /D<name>{=|#}<text> 定义宏
/E 将预处理定向到 stdout                /EP 预处理到标准输出,没有 #line
/P 预处理到文件                         /Fx 将插入的代码合并到文件中
/FI<file> 命名强制包含文件              /U<name> 移除预定义的宏
/u 移除所有预定义的宏                   /I<dir> 添加到包含搜索路径
/X 忽略“标准位置”                    

                                -语言-
/Zi 启用调试信息                        /Z7 启用旧式调试信息
/Zp[n] 在 n 字节边界上包装结构          /Za 禁用扩展
/Ze 启用扩展(默认)                      /Zl 忽略 .OBJ 中的默认库名
/Zg 生成函数原型                        /Zs 只进行语法检查
/vd{0|1|2} 禁用/启用 vtordisp           /vm<x> 指向成员的指针类型
/Zc:arg1[,arg2] C++ 语言合规性,这里的参数可以是:
    forScope[-] - 对范围规则强制使用标准 C++
    wchar_t[-] - wchar_t 是本机类型,不是 typedef
/ZI 启用“编辑并继续”调试信息          /openmp 启用 OpenMP 2.0 语言扩展

                              - 杂项 -
@<file> 选项响应文件                    /?, /help 打印此帮助消息
/bigobj 生成扩展的对象格式              /c 只编译,不链接
/errorReport:option 将内部编译器错误报告给 Microsoft
    none - 不发送报告                       prompt - 提示立即发送报告
    queue - 在下一次管理员登录时,提示发送报告(默认)
    send - 自动发送报告                 /FC 诊断中使用完整路径名
/H<num> 最大外部名称长度                /J 默认 char 类型是 unsigned
/MP[n] 最多使用“n”个进程进行编译      /nologo 取消显示版权消息
/showIncludes 显示包含文件名            /Tc<source file> 将文件编译为 .c
/Tp<source file> 将文件编译为 .cpp      /TC 将所有文件编译为 .c
/TP 将所有文件编译为 .cpp               /V<string> 设置版本字符串
/w 禁用所有警告                         /wd<n> 禁用警告 n
/we<n> 将警告 n 视为错误                /wo<n> 发出一次警告 n
/w<l><n> 为 n 设置警告等级 1-4          /W<n> 设置警告等级(默认 n=1)
/Wall 启用所有警告                      /WL 启用单行诊断
/WX 将警告视为错误                      /Yc[file] 创建 .PCH 文件
/Yd 将调试信息放在每个 .OBJ 中          /Yl[sym] 为调试库插入 .PCH 引用
/Yu[file] 使用 .PCH 文件                /Y- 禁用所有 PCH 选项
/Zm<n> 最大内存分配(默认为 %)           /Wp64 启用 64 位端口定位警告

                                -链接-
/LD 创建 .DLL                           /LDd 创建 .DLL 调试库
/LN 创建 .netmodule                     /F<num> 设置堆栈大小
/link [链接器选项和库]                  /MD 与 MSVCRT.LIB 链接
/MT 与 LIBCMT.LIB 链接                  /MDd 与 MSVCRTD.LIB 调试库链接
/MTd 与 LIBCMTD.LIB 调试库链接        

                         -代码分析-
/analyze[:WX-] 启用代码分析            
    WX- - 即使调用了 /WX,也不应将代码分析警告视为错误


QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 9楼 发表于: 2012-04-10
Visual C/C++编译器使用心得
简介


我将在此文章中探索一点 Visual C/C++ 编译器背后的细节。个别编译器的开关选项是怎样影响代码生成的。


背景


在此我假设读者熟悉 Visual C/C++ 编译器的基础。同时本文的读者也不怕使用编译器的命令行工具。


Enable Read-Only String Pooling


命令行通过使用 “enable read-only string pooling” 能有打开 /GF 编译器开关。但这究竟意味着什么呢?


在 nutshell 中它意味着在源码中不同位置具有相同字面值的String将会在二进制映像中被转换成单一的数据项。因此 /GF 选项将帮助你优化代码,因为它将产生更小的二进制码。让我们来看看以下代码段:


01
char* str1 = "Bart Simpson";
02
char* str2 = "Milhouse van Houten";
03

04
void foo() {
05
   static char* s1 = "Bart Simpson";
06
   static char* s2 = "Milhouse van Houten";
07
   //...
08
}
09

10
int main() {
11
   //...
12
}
没有指定 /GF 选项,Visual C/C++ 编译器将生成如下代码:


01
_DATA   SEGMENT
02
$SG855  DB    'Bart Simpson', 00H
03
        ORG $+3
04
str1    DQ    FLAT:$SG855
05
$SG857  DB    'Milhouse van Houten', 00H
06
        ORG $+4
07
str2    DQ    FLAT:$SG857
08
$SG862  DB    'Bart Simpson', 00H
09
        ORG $+3
10
?s1@?1??foo@@9@9 DQ FLAT:$SG862
11
$SG865  DB    'Milhouse van Houten', 00H
12
        ORG $+4
13
?s2@?1??foo@@9@9 DQ FLAT:$SG865
14
_DATA   ENDS
你可以看到每一个字符串值都被单独地放入二进制映像中,即使某些字面值是相同的。


01
SECTION HEADER #3
02
   .data name
03
    21A0 virtual size
04
    9000 virtual address (0000000140009000 to 000000014000B19F)
05
    1000 size of raw data
06
    7800 file pointer to raw data (00007800 to 000087FF)
07
       0 file pointer to relocation table
08
       0 file pointer to line numbers
09
       0 number of relocations
10
       0 number of line numbers
11
C0000040 flags
12
         Initialized Data
13
         Read Write
14
RAW DATA #3
15
  0000000140009000: 42 61 72 74 20 53 69 6D 70 73 6F 6E 00 00 00 00  Bart Simpson....
16
  0000000140009010: 00 90 00 40 01 00 00 00 4D 69 6C 68 6F 75 73 65  ...@....Milhouse
17
  0000000140009020: 20 76 61 6E 20 48 6F 75 74 65 6E 00 00 00 00 00   van Houten.....
18
  0000000140009030: 18 90 00 40 01 00 00 00 42 61 72 74 20 53 69 6D  ...@....Bart Sim
19
  0000000140009040: 70 73 6F 6E 00 00 00 00 38 90 00 40 01 00 00 00  pson....8..@....
20
  0000000140009050: 4D 69 6C 68 6F 75 73 65 20 76 61 6E 20 48 6F 75  Milhouse van Hou
21
  0000000140009060: 74 65 6E 00
现在打开 /GF 编译器开关来重建代码,正如你看到的以下的汇编列表,编译器将生成完全不同的代码:


01
CONST    SEGMENT
02
   ??_C@_0BE@BMDGJIMK@Milhouse?5van?5Houten?$AA@ DB 'Milhouse van Houten', 00H
03
CONST    ENDS
04

05
CONST    SEGMENT
06
   ??_C@_0N@MPADFJH@Bart?5Simpson?$AA@ DB 'Bart Simpson', 00H
07
CONST    ENDS
08

09
_DATA    SEGMENT
10
   str1    DQ    FLAT:??_C@_0N@MPADFJH@Bart?5Simpson?$AA@
11
   str2    DQ    FLAT:??_C@_0BE@BMDGJIMK@Milhouse?5van?5Houten?$AA@
12
   ?s1@?1??foo@@9@9 DQ FLAT:??_C@_0N@MPADFJH@Bart?5Simpson?$AA@
13
   ?s2@?1??foo@@9@9 DQ FLAT:??_C@_0BE@BMDGJIMK@Milhouse?5van?5Houten?$AA@
14
_DATA    ENDS
我们观察到两点:


一些字面值完全相同的字符串将会被转换成单一的数据项
字符串数据将会被放置在只读数据段
01
SECTION HEADER #2
02
  .rdata name
03
    2560 virtual size
04
    6000 virtual address (0000000140006000 to 000000014000855F)
05
    2600 size of raw data
06
    5200 file pointer to raw data (00005200 to 000077FF)
07
       0 file pointer to relocation table
08
       0 file pointer to line numbers
09
       0 number of relocations
10
       0 number of line numbers
11
40000040 flags
12
         Initialized Data
13
         Read Only
14

15
RAW DATA #2
16
  0000000140006000: 48 81 00 00 00 00 00 00 5A 81 00 00 00 00 00 00  H.......Z.......
17
  ...
18
  0000000140006210: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
19
  0000000140006220: 4D 69 6C 68 6F 75 73 65 20 76 61 6E 20 48 6F 75  Milhouse van Hou
20
  0000000140006230: 74 65 6E 00 00 00 00 00 42 61 72 74 20 53 69 6D  ten.....Bart Sim
21
  0000000140006240: 70 73 6F 6E 00
亮点


C/C++ 编程语言中的字符串概念甚至还困扰着一些高级开发者,你可以找到跟字符串相关的数不尽的问题。此处只指出了此文章代码中涉及到的问题:


依据带注释的C++参考手册解释,一个字符串值具有 char[] 类型(字符串数组)。想要修改一个字符串值将导致未定义行为。C++ 语言禁止将字符串值定义为 const char[] 类型以适应经典C。


让我们看看将字符串定义为 char[] 而不是 char*后编译器是怎么做的:


01
char str1[] = "Bart Simpson";
02
char str2[] = "Milhouse van Houten";
03

04
void foo() {
05
   static char s1[] = "Bart Simpson";
06
   static char s2[] = "Milhouse van Houten";
07
   //...
08
}
09

10
int main() {
11
   //...
12
}
令人惊讶的是 /GF 选项在这中情况下是无效的!多数据项将被放置在二进制映像的数据段。这不是一个关于编译器的真实问题。只是某人想要期待专业编译器能做的一件事而已...
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
描述
快速回复

您目前还是游客,请 登录注册
批量上传需要先选择文件,再选择上传