CATEGORY / Development

乱用 STL 是地狱

根据《Effective STL》条款21中的例子,建立一个比较类型为 less_equal 的 set 容器:

    set< int, less_equal<int> > s;

然后连续插入两个10:

    s.insert(10); // 10a
    s.insert(10); // 10b

会得到什么?

在debug下,可能会给出一个assert报比较符号不合法,第二次插入失败,但在release下,这个动作很可能是未定义的,而通常的结果是,set中存在了两个键值同为10的项,也就是说,set被悄声无息地变成了multiset!太可怕了!

所以,为正确的容器挑选正确的比较函数,很重要。用好 STL 其实并不容易,用错了不仅执行效率狂低,而且还可能出现这些难以想象的意外……


do...while(0) 的妙用

在 C++ 中,有三种类型的循环语句:for、while 和 do...while,但是在一般应用中作循环时,我们可能用 for 和 while 要多一些,do...while 相对不受重视。
但是,最近在读我们项目的代码时,却发现了 do...while 的一些十分聪明的用法,不是用来做循环,而是用作其他来提高代码的健壮性。

CONTINUE READING »


让 C++ 的 new 操作失败时返回空指针

C 中如果创建一个对象失败,就会返回空指针。但是对于 C++ 就不一样了,new 是不应返回空指针的,书上的推荐做法是在构造函数里抛异常。
当不想引入异常机制的时候,一般的做法是在构造器里啥都不做(最多做个变量初始化),加一个 Init() 函数来完成真正的初始化工作。
然而这样就使得每次创建一个对象,都要执行两步(new+init),总不是太方便,其实 C++ 的 new 操作符是带参的,可以通过“new(std::nothrow) CXxx”的方式让 new 失败时返回 null 指针,来标记失败(而不是抛出异常)。


严重避免在构造/析构过程中调用虚函数

之前只知道在 C++ 的构造器中,调用虚函数(非纯虚函数)不会出现多态的情况,却想当然认为既然析构器可以被声明为虚“函数”,那么析构器应该是能实现虚函数调用的的多态结果的。
事实证明,我又被蒙骗了!

构造器/析构器不会在调用虚函数时执行子类的重载实现,而且更危险的是当构造器/析构器“间接地”调用了虚函数(例如调用了一个非虚函数,但这个函数里调用了虚函数),不仅子类的重载实现不会被执行,而且还很难发现这种 bug。

所以,切记,切忌在构造器/析构器中直接或间接地调用任何虚函数!(单是在构造器/析构器中直接或间接地调用其他对象的虚函数并不受影响)


PBRT 学习:安装编译

去年在前公司看着 leader 用 PBRT 做基于 DX11/SM5.0 的 ray tracing 实验时,还懵懂得云里雾里。而我现在的 manager,在读研的时候就已经在研究 PBRT 了。我, out 了!最近由于工作的原因,终于开始着手学习 PBRT,而这玩意第一道关卡就是安装编译中的一堆问题,记录以备不时只需。

网上关于 PBRT 的资料基本都是 1.03 或更早的,而且基本都跳过了自动生成代码的预处理阶段。这次下载来的最新 pbrt-src-1.04.zip for Windows 虽然在其 release note 里说“a number of bugs and incompatabilities have been fixed”,但从我安装编译的经过来看,问题似乎更多了(斯坦福的大大们不应该这么粗心吧)。

首先把下载的文件解压到任意目录下,我这是“D:\Program Files\pbrt-1.04”,不过推荐还是放到分区根目录或者文件夹名不含空格的路径下,否则后面会多几个体力活。

PBRT 使用了 Bison 和 Flex 这两个工具来生成用于解析 pbrt 脚本文件的代码文件(这话有点绕哈),而这两个工具本是 Linux 下的,现在都有大大做了 Windows 版的移植(Bison for WindowsFlex for Windows)。分别把 Bison Binaries 中的 \bin 和 \share、Bison Dependencies 中的 \bin、Flex Binaries 中的 \bin 目录解压到任意目录下,我这是“D:\GnuWin32”。

这时 PBRT 还是不能编译的,原因是 1.04 中移除了对 OpenEXR 工具包的包含。OpenEXR 本身是三大 HDRI 格式之一,另外两种格式在以前我都处理过,而对 EXR 格式不熟,这里暂时也不做深究。OpenEXR 工具包可以在其官网下载(最新的 1.5.0 没有 VS2005 的预编译版本,我偷懒就直接下 openexr-1.4.0-vs2005.zip 了),也可以从 PBRT 1.03 zip 包中直接拿来用,然后整个解压出来,我这是“D:\Program Files\pbrt-1.04\openexr-1.4.0-vs2005”。这还没完,无论是官网还是 PBRT 1.03 里的 OpenEXR 工具包,在 \lib 文件夹下都缺少 zdll.lib 这个文件,去 zlib 首页下载 zlib compiled DLL zipfile,把压缩包中的 \lib 解压到 OpenEXR 所在文件夹下。

CONTINUE READING »


#ifdef 中的逻辑与或操作

原本用宏定义包起来的代码类似如下:

#ifndef A
// codes
#endif // A

现在要加入一个宏定义 B,实现类似这样的条件判断(显然实际上这样是不行的):

#ifndef A && ifdef B

其实应该这样:

#if (!defined A) && (defined B)
// codes
#endif // !A && B

这就修正了之前一直以为的“既生 #ifdef,何生 #if defined”的思维,其实还是有差别的


让 Visual Studio 2005 自动生成 Manifest

微软同学永远是个把简单问题复杂化的孩子,这不,当年为了在 Windows XP 中同时支持两套控件风格(新的 XP 风格和旧的 95/98 风格),“发明”了 .manifest 这么个玩意,使以前的老程序也能自动使用上新的控件风格。

然而在 VS 里,微软并不是总是默认帮我们自动生成这个破玩意儿。最早的办法就是手写一个 .manifest XML 文件,不过这个办法在 VS 2005 编译出来的程序里似乎并不起作用。当然,新方法总是随之而出的,而且很“简单”(绕了一大圈又回来了):

在 Project Properties 对话框的 Configuration Properties | Linker | Manifest File | Additional Manifest Dependencies 选项里,填入:

"type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='6595b64144ccf1df' language='*'"

或者在程序里直接写下如下代码:

#pragma comment(linker, "\"/manifestdependency:type='Win32' name='Test.Research.SampleAssembly' version='6.0.0.0' processorArchitecture='X86' publicKeyToken='0000000000000000' language='*'\"")

太阳又照常东升西落了 :)


Visual Studio 2008 无法进入修复/卸载界面的解决办法

最近需要在家办公,硬盘空间又不够了,只好卸了 VS2008 重装 VS2005,结果无论通过控制面板的卸载还是通过光盘的 autorun.exe,都在检查配置的时候弹出“A problem has been encountered while loading the setup components. Canceling setup.”的错误对话框,然后就自动退出了,连修复的机会都完全不给……

放狗在 MSDN 的论坛里搜到了这篇帖子里 George175 给出的解决办法,原来是 VS2008 的一个 patch 捣的乱,直接进控制面板,进入添加/删除程序(或卸载程序),左边选择查看/卸载更新,找到“Hot Fix for Visual Studio 2008”这个玩意儿,直接咔嚓掉。

然后,VS2008 就能正常卸载了。

另,微软的一位同学说安装 VS2008 SP1 也能解决,没有验证,应该不会有错。


用 前置声明,还是用 #include?

为了能让编译速度快一点,明智之举是使用前置声明,而不是 #include 整个头文件。

那声明时候可以用前置声明,而什么时候必须 #include 头文件呢?简单的说:

  1. 当不需要用到类型的具体实现时,包括构造器赋值运算符成员函数等,只需要前置声明就可以了
  2. 当需要用到类型的以上方法时,就不得不 #include 整个头文件

对该技巧的具体分析可以参考这里这里


征服 Visual Studio 的 Editor

  • 块选中
  • 类似 UltraEdit 里的“列模式”,按住 Alt 键再移动光标,就可以选择一块范围内的内容,而不必被束缚在只能一行接一行的选中方式了。

  • 整行剪切 / 复制 / 粘贴
  • 平时我都是用鼠标移到一行最左边的行号区去选中一行,然后执行复制或拖动等操作,其实,只要光标在某一行,直接按 Ctrl + X / Ctrl + C,就等于剪切/复制整行了,非常方便。

  • Clipboard Ring
  • 一直以来,当需要在多个文件中复制粘贴多个内容时,我都很笨地挨个 Ctrl + C / Ctrl + V,从来也懒得想是不是该搞个 multi-clipboard 工具来提高效率 -,-|||

    其实,从 VS2003 开始,VS 就已经内置了多重剪贴板的功能,虽然只支持当前 VS 进程内多文件间的复制粘贴,这就是 Clipboard Ring。Clipboard Ring 采用 LIFO(后进先出)的方式组织,即最后被复制或粘贴的内容排在环的最前面,最大支持 10 块剪贴板。用法很简单,对需要复制的多个内容块按 Ctrl + C,然后到需要粘贴的地方按 Ctrl + Shift + V,选择需要粘贴的内容就 Ok 了。每个 VS 版本的 Clipboard Ring 操作略有不同,在此不赘述了。

  • 代码重构
  • 待续……


Page 2 of 41234