TimothyQiu's Blog

keep it simple stupid

几个 C++ 异常的零碎知识

分类:技术

在派生类构造函数中捕获基类构造函数异常

class Base {
public:
    Base()
    {
        throw std::runtime_error("Error from Base");
    }
};

class Derived : public Base {
public:
    Derived()
    {
        try {
        } catch(std::exception const& e) {
        }
    }
};

类似于这样的代码,是捕获不到异常的,因为执行到构造函数的函数体中时,基类已经构造完毕了。

索性 C艹 有一个神奇的写法:

Derived::Derived()
try : Base() {
} catch(...) {
}

没错,我没有漏写大括号……你完全可以直接把一个 try {} catch {} 当作函数体……同理,对于一般函数这么写也行:

void foo()
try {
} catch(...) {
}

当然,构造函数的 catch 里是无论如何都会帮你确保有一个异常被抛出去的。(你不显式抛,它就帮你把被捕获的异常抛出去。)

后来发现这个东西学名叫做:Function-try block。

C++11 的异常处理

查了一番,似乎大的改变在于:

先看看之前的 Dynamic Exception Specification 是什么:

void foo();              // (1) 允许抛出异常
void foo() throw(X, Y);  // (2) 只允许抛出 X 和 Y 异常
void foo() throw();      // (3) 不允许抛出异常

坑爹之处在于第 2 点:Dynamic Exception Specification 是在运行时进行的,于是,为了确保只能抛出指定类型的异常,就会生成额外代码降低执行效率。

最扭曲的是可自定义的 unexcepted 处理函数。所谓 unexcepted 处理函数,就是一旦你抛出了 throw(...) 中没有出现的类型,系统会去调用的那个函数。默认的 unexcepted 函数直接调用 std::terminate() 结束程序;如果你觉得这不好,你可能会想要去替换掉这个全局的处理函数,你会发现你可以:

  1. 和默认行为一样,调用 std::terminate() 自尽。
  2. 抛出异常,但是:
    1. 这个异常存在于 throw(...) 列表中,那么正常抛出。
    2. 这个异常不存在于列表中,但是 std::bad_exception 在,那么抛 std::bad_exception
    3. 这个异常不存在于列表中,而且 std::bad_exception 也不在,那么系统 std::terminate() 之。

重点在于,这是一个「全局」的处理函数。如果有人自定义这个,相当于如果要写 throw 列表,就要把 std::bad_exception 加上。

于是 C++11 因为这个在实践中基本没人用的东西而废弃了这个名字很长的功能,用 noexcept 取而代之:

void foo();                                 // (1) 允许抛出异常,相当于 noexcept(false)
void foo() noexcept(constant-expression);   // (2) expression 成立时不允许抛出异常
void foo() noexcept;                        // (3) 不允许抛出异常,相当于 noexcept(true)

也就是将原先的 (2) 去掉,然后扩展一下原先的 (3)。一旦不允许抛异常的函数抛出了异常,那么直接 std::terminate() 结束运行。

对应还有一个同名的操作符 noexcept,用来在编译时判断表达式是否允许抛出异常(遇到函数时只通过其声明时的 noexcept 与否判断)。

noexcept(1 + 2)     // false
noexcept(throw 1)   // true
noexcept(foo())     // 返回 foo() 声明时的 noexcept 与否

最好在哪里捕获异常

处处都 catch 的话,就反而比 if 麻烦了。据推荐,最好在这些地方 catch

参考

C++11 Variadic Template

分类:技术

听说这个特性是很久以前了,总是读作「维拉迪克·坦普雷特」,一直没反应过来中文到底该叫什么,因为 C 时代的 Variadic Macro 我一直是很象形地读作「点点点」的 = =||

OK,扯远了。Variadic Template 对应中文应该是「可变参数模板」。

阅读剩余部分...

字符动画的制作方法

分类:技术

字符画版本的 Bad Apple 的流行应该是好多年以前的事情了吧……虽然当时也想过自己做一个,但一直觉得图片转字符画是一个很神秘的过程(据说 mplayer 可以直接把视频按字符画输出)。前两天做完 ssoaag 发现,貌似可以用这个东西的原理输出字符动画,于是就有了下面这个视频 = =

【似乎是教程】如何制作字符动画

看到最后的效果你会发现最终产出的字符动画最右一列字符是略有问题的,在视频里也注明了,是那个 GetColor 函数里有个失误 = = 会导致某些情况下取到下一行的颜色,需要 continue 掉,并且把最后除以的 w * h 变成真正有效的像素数。

SSOAAG

分类:技术

好吧,标题的意思其实是 Some Sort Of Ascii Art Generator……

把这两天的无聊成果放 GitHub 上了,按标题的意思可以理解为某种字符画生成器。

一句话解释原理

在一个 HTML 文档里,把一系列「■」字符作为像素点,并用 <span> 元素为每一个字符指定颜色。再把行距、字间距变小,就形成了一个画布。把图片中每一个像素的颜色赋值给对应的「■」字符就可以产出该图片的马赛克版本(类似于放大 N 倍后的效果)。而根据这个像素点的颜色值可以计算出灰度,再根据灰度的深浅选择相应的字符代替「■」字符(比如灰度小于 64 选 @、小于 128 选 :、其余选 .),就可以产出该图片的字符画版本了。

好吧,这不是一句话,这是一段话 Orz...

Bitmap

嗯,挑了个很矬的名字命名这个类。Bitmap 类主要是为了读取位图文件中的颜色数据而生的。在 Load 时解析位图文件内容,无论原来是什么格式,内部都用 0xAARRGGBB 的形式保存……………………(这是设想)

现实:色深和压缩方式好多……于是目前只把最简单的未压缩版本的 1位色(黑白)/ 4位色 / 8位色 / 16位色 / 24 位色 / 32 位色位图读取实现了。

于是 DIB Header 是 BITMAPINFOHEADER 版本及以后的未压缩位图应该都可以读取了,理论上 4 色位图也支持读取,但我真心没有找到可以产出一个 4 色位图的办法,汗~

程序崩溃的善后工作

分类:技术

说来,写 C/C++ 的程序,由于指针的存在,程序崩溃什么的也就没什么大惊小怪的了。人非圣贤,孰能无过嘛,而且个人觉得程序崩溃比出现错误的结果好调试多了:在 Visual Studio 里 Debug 版本 F5 调试运行直接可以断在崩溃的地方,方便调试。但 Release 版本就没这么幸运了 :(

如果说单纯是是调试 Release 版本,我只用过《游戏之旅》中介绍的勾选 Linker 选项中的 Generate Map File,然后通过崩溃提示信息中提供的 EIP 查这个 Map File 找到崩在哪个函数里,兴致高一点的根据反汇编一步步走下去兴许还能知道是崩在哪句上 :)

不过说到最终交付出去的程序,面对可能存在的各种未知问题,还是生成 Dump 文件,把崩溃那一刻的信息写进文件以供日后分析比较靠谱。

阅读剩余部分...