《深度探索C++对象模型》学习笔记(1-4章)

今年静下心来重读了侯捷老师译本的Lippman大神作品《Inside the C++ Object Model》,记录下学习过程中的疑问和心得有助于以后……面试。不是开玩笑,这本书里面随便找一段出来都可以当作C++面试题,学透彻了的话,无论是面试别人或者被人面试都是相当犀利的武器。

第1章 对象

对象

OOP的成本

  • 实际上相对于struct来讲,class只是程序员友好的手段
  • 单纯使用class以及继承,内存成本并不会增加,成员函数也会由编译器进行转换(4.1函数成员:调用)
  • 真正需要额外成本的是virtual特性。

对象布局方式:简单对象模型

  • 思想:尽量降低实现的复杂度
  • 每个对象中的项目都是一个slot,按照顺序排列
  • 数据成员本身并不放置在对象中,只保存指针,避免变长对象出现
  • 没有用于实际产品中(但成为指向成员的指针的灵感来源)

对象布局方式:表格驱动的对象模型

  • 对象保存两个指针,一个指向拥有所有数据成员指针的表格,一个指向拥有所有成员函数指针的表格
  • 也没用于实际产品中(但指向函数指针表格的指针,演化为vptr)

对象布局方式:C++对象模型

  • 演化自简单对象模型
  • 非静态成员存储于类内。静态的则都位于之外。
  • vptr和vtbl的概念。用于支持RTTI的type_info位于vtble第一个slot内

继承的情况

  • 简单模型:子类描述基类的slot保存基类指针,优点是父类的修改不会影响子类
  • 另一模型:保存一个基类表格,子类拥有一个bptr指向之。表中的slot再指向具体的基类

差异

关键字差异

  • class和struct的思考:为了兼容C而多做了很多工作
  • struct表现数据集合体,而class为OOP而生
  • class的多个section不能保证成员在内存中的顺序和声明的一样
  • struct目前的作用,抽取class的一部分作为参数传给C函数

对象差异

  • 三种programming paradigms
    1. procedural model,C语言的函数调用
    2. ADT model,隐含的调用,例如等号运算符
    3. object-oriented model,面向对象特性
  • 多态的实现需要由指针或引用来实现,但C++中的指针和引用并非多态的必然结果
  • 支持多态的方法
  • 指针的转换(基类子类)
  • 虚函数调用
  • dynamic_cast或者typeid运算符

指针和多态

  • void*虽然可以指向某地址,但并不清楚具体类型,也不能通过它操作对象
  • D继承自B,D的大小为B的子对象加上D特有的部分
  • D*和B*的指针对象虽然都指向同一个开始地址(B的开头),但它们涵盖的范围不同
  • 指针在编译期会决定固定的可用接口(public)以及接口对应的访问范围,这类信息都在链接(link)中,它位于vptr和其指向的vtbl之间
  • 编译器在构造和赋值时候会决定vptr的值。如果对象含有多于1个的vptr,那么就不用基类指针进行覆盖(5.4构析拷贝:拷贝赋值)

Read more >>>

《ギャラリーフェイク》(赝品画廊)

最近虽然PSV热销得紧,但考虑了一下还是没有预定,因为只要有AVG在,PSP还能再战五百年。能否将手头正在攻略中的游戏打完暂且不提,还有过去已经发布的AVG,再加上现在AVG不说质量如何先把文字量和游戏时间提上去了,以后很长一段时间都可以用来回顾漏过的好游戏。

近几天在玩的《ギャラリーフェイク》(赝品画廊)是漫画、动画和游戏的三栖作品,原作由細野不二彦在小学馆的《Big Comic Spirits》上连载,并且得过小学馆漫画大赏(顺带一提《墨攻》原作、《20世纪少年》、《深夜食堂》和《少年犯之七人》都曾拿过此奖)。在连载终结的05年被连续改编成动画和游戏,而且改编作基本都是踩着前作肩膀出现,这种规划显然是要一口气扩大作品的影响。漫画版第一眼看上去还以为是80年代的作品,有作画风格的因素,也有整体色彩给人的第一印象,但作画本社质量不错,总体来说可算差强人意,又有三位大牌(森川智之、雪野五月和川澄绫子)坐镇,让人觉得还是有兴趣看完。

今天主要说的是游戏版的Gallery Fake,09年由PCPChina进行汉化。游戏版采用的是对话式推进游戏:

  1. 需要玩家在出现选择肢时候判断正确才能继续游戏,典型的一条胡同头走到黑的设计,没有任何分支,也不像《逆转裁判》那样有伪多分支的设计。
  2. 选择肢内容为对话中出现过的线索。最多五项,可能是抽象的文字线索,也可能是实在物体。
  3. 选择肢出现的场景为和游戏中其他任务进行对话寻找更新线索,对话对象可能是对手,也可能是同伴(暂时还没看到自言自语)。需要判断对话方哪一句有问题,然后用已有线索进行击破(break)。
  4. 有血条,选择错误扣除固定的1格,全部扣完后Game Over。但关键对话存活后会回血3格。

在《逆转裁判》出来之后,类似的AVG都会让人不得不拿它做个标杆进行比较,况且Gallery Fake本身还和《逆转》一样是带有主角光环的男一号+捡来的女一号这种配对。从人物设计上Gallery Fake可谓成功,这要归功于原作者,而身世背景的交代也很顺利,随着第一个故事的进行玩家可以大概了解游戏中几位主要人物的身份,同时留了一些谜作为伏笔在后续故事中展开。相比较《逆转》而言,Gallery Fake的单元剧形式同样突出主角们的特色,其他路人一笔带过,但目前为止玩到Story 4,暂时还未见到几个故事产生关联,也还没有什么出人意料的展开。如果直到最后各章还是完全独立的话,的确是个缺憾。有关联哦!

作品的背景拜原作所赐,属于比较特别的类型。艺术品鉴定本身就是一门专业性极强的科目,作品主题又涉及到敏感的赝品,所以肯定少不了黑道、警察这类的介入,但作品对这方面拿捏得十分到位,以“赝品”“复制品”为核心展开的故事包括但不限于艺术品犯罪,而且穿插故事中还有大量的艺术品和古玩知识。故事本身不好评价太多,因为必然会涉及剧透,但可以透露的是慢热类型,故事越来越好看,涉及到的艺术家和艺术史典故也越来越多,虽然可以说都是单线任务,但其故事性丝毫不差。

游戏的作画非常漂亮!背景的图细节毫不马虎,如下图的菩萨像:

其他的类似于城市街道、机场、室内场景都是如此,这一点上我想给10分。游戏版本和动画版本的人设还原度很高(除了萨拉的肤色……),如果是原作Fans的话应该能感觉到统一感,但游戏版上色有些生硬,或许是我个人喜好问题,觉得这种人物形象和背景并不是特别搭调。总体来说立绘比较生动,主角们的表情和动作变化也很有趣,作为AVG能做到这一点就是成功了。

游戏的BGM很精彩,配合艺术品题材当然要用西洋乐,因为刚开始补动画版,所以不知道OST是否统一,但游戏版本的已经足够让人满足。最近玩的一些不太热门的AVG,其OST都相当好,让人有收CD的欲望。

最后说说游戏系统,前面大概列了一下游戏关键的选择肢相关内容,个人来说,非常不喜欢游戏版的这套互动,让我没法给这个游戏打高分的原因也在于此。首先觉得不爽的是所持线索太少,只有五个,而且多数是话语线索。线索基本无法重用(有极少会用完后再次提及)。选择方式中单选的分支过于简单,反之多选的部分因为选项意思过于暧昧的问题又太让人抓狂:

这里不得不剧透一下,请原谅我。上面这个分支中要求选择“提示两套纸牌均为一人所作”的线索。其中第四条的详细内容大意为“此人做了两套纸牌”。结果正确答案中并没有此项,你可以看到几乎血条都被掏空了我才用近似穷举的方式艰难过关。类似这样的情况在游戏中还有不少——连需要选择break的对话部分都会有这种情况出现,有时候会白白费血。

游戏版的诚意足够,除了坑人的互动系统外,是个好宣传,能让没有看过原作或者动画版的玩家有兴趣回过头补完作品的其他版本。所以推荐一下。

指针参数传递和.Net中String类型的表现

本来不想对这两个老掉牙的问题进行讨论,但联系起来看确实有用。先看代码:

    class Program {
        static void Foo( string s ) {
            s = "变了";
        }

        static void Main( string[] args ) {
            string s = "原始String";
            Foo( s );
            Console.WriteLine( s );
            Console.ReadKey();
        }
    }

没错,这就是最经典的“作为引用类型的String类传参时候表现值类型特点”的例子。上述程序意料之中输出为“原始String”,但其实这并不是因为String自身特殊到所谓“引用类型表现值类型特点”,为什么这么说?看下面这个例子:

    class Person {
        public String Name;
    }

    class Program {
        static void Foo( Person p ) {
            p = new Person { Name = "李四" };
        }

        static void Main( string[] args ) {
            Person p = new Person { Name = "张三" };
            Foo( p );
            Console.WriteLine( p.Name );
            Console.ReadKey();
        }
    }

这次就清楚多了,基本都能看出输出的是“张三”,关键在于自定义class用了new这个关键字、而String类的等号便是最大的迷惑点。所以说String类在当作参数传递的时候和其他引用类型并无二致。

作为本地参数的s和p同原来的变量是完全不同的引用存在,只不过引用到的是同一块内存。使用new或者重载的=进行新内存分配后,原来的变量指向并没有发生变化。

好,那么就引出标题里面的第一个问题了“指针参数传递”,在C/C++中很容易见到这样的错误:

void Foo( int* a, int* b )
    {
    int* tmp = a;
    a = b;
    b = tmp;
    }

int main(int argc, char *argv[])
    {
    int a = 0;
    int b = 5;
    Foo( &a, &b );
    cout<<a<<","<<b<<endl;
    }

对于形参a和b而言,它们只是在栈上对于原本变量的一份拷贝,指向同一块内存,改变它的指向是没有意义的。

a(变量) --|
         |--->  0
a(参数) --|

如果没有在这上面碰过钉子的请受我一拜。如果希望改变原有指针的内容,需要直接改变被指向区域的值或者传递二级指针:

void Foo( int* a, int* b )
    {
    int tmp = *a;
    *a = *b;
    *b = tmp;
    }

void FooAlloc( int** p, int size )
    {
    *p = malloc( sizeof( int ) * size );
    }

可以说这个问题和开始提到的String的特殊表现殊途同归:

  • 归根结底,不管是指针还是引用,当作参数进行传递的时候仍然是值。
  • 指针本身的操作和指针指向内容的操作是不同的,星星太多就容易乱。同理的还有const int* i和int* const i。

还是那句话,String并不特殊,它在参数传递上表现得和其他引用类型没什么不同,但不同点在于String是一个immutable类型,所有对其进行的修改,例如SubString、连接(+)、赋值(=)等等,都会当作创建新拷贝的操作对待,这也就是导致String表现出类似于值类型特点的原因。

参考资料

Windows Phone上为WebBrowser增加链接的Context Menu

(似乎这篇文章被垃圾评论认准了,所以无奈关评)

问题

对于浏览器而言,Context Menu使用起来是用户操作页面元素最直接、同时也是对页面执行各种操作中路径最短的。下面这个是系统自带浏览器的效果:

可惜如大家所见,WebBrowser控件不支持这个功能。原本以为会有个OnLinkPressed或者OnImageSelected之类的回调供开发者使用,结果到了Mango一看,还是没有。但前两天下载了UC浏览器,发现它实现了这个功能:

从效果来看,很显然微软没有给UC开小灶,而是UC的工程师凭借自己努力搞出来的,原因有二:

  • 这个菜单只有在页面完全载入后才能按出来
  • 菜单的触发灵敏程度不稳定,有的时候长按N秒没有反应;有的时候手指移动到链接上动一下就出来了。

实现

据此猜测,UC浏览器用的是注入Javascript的方式实现上下文菜单的互动的。OK,在(我力所能及的)其他方法行不通的情况下,那我们跟也根据这个思路去实现这个功能。既然目标明确,那么需要实现的具体功能也很明确:

  • 用户长按页面链接(暂且只支持链接,图片之类可以举一反三)时,WebBrowser可以通知code behind事件发生
  • 事件通知的同时还需要带上所点击链接的文本和Url属性
  • 在用户所长按位置出现Context Menu,点击具体的Menu Item则利用文本和Url进行后续操作(例如新页面打开、加入收藏等)

和桌面的.net 平台一样,WP7的WebBrowser控件也是通过Navigate方法和Navigated回调进行页面控制的。对于前端页面事件回调,自然率先想到的就是WebBrowser::ScriptNotify()回调,试一下如何?在页面里面写入这样的内容(刚发现blog没装代码高亮插件……):

<a href="#" onclick="javascript:window.external.notify('hi')";>你好</a>

后台绑定方法:

private void webBrowser_ScriptNotify(object sender, NotifyEventArgs e)  {
    MessageBox.Show( e.value.ToString() );
}

效果不错。继续考虑实现,注入Script则使用InvokeScript。这个方法有两个版本,分别带有1个和2个参数,前者直接执行页面中的函数;后者则是带有参数执行。很显然我们要操作的是外部的网页,并非自己的HTML页面,显然外部的页面是不会如此好心帮你给每一个<a>加上需要的代码执行事件,那么此时就需要Javascript一个特殊的函数eval。eval函数带有一个参数,可以将传入的字符串当作代码执行。很好,于是我们可以用InvokeScript两个参数的版本:

// 函数声明不写了,我把这个函数命名为InjectScripts()
                String script =
                @"
                window.onLinkPressed = function() {
                    var elem = event.srcElement;
                    if ( elem != null ) {
                        window.external.notify( elem.getAttribute('href') + '|' + elem.innerHTML );
                    }
                    return false;
                };

                window.doBindLinks = function() {
                    var elems = document.getElementsByTagName('a');
                    for (var i = 0; i < elems.length; i++) {
                        var elem = elems[i];
                        elem.attachEvent('onmousedown', onLinkPressed);
                    }
                };";
            webBrowser.InvokeScript( "eval", script );
            webBrowser.InvokeScript( functionName );

// 可以在页面初始化时候绑定好,页面载入后注入脚本
            webBrowser.Navigated += ( s, e ) => {
                if ( e.Uri.OriginalString.StartsWith( "http" ) ) {
                    InjectScripts();
                }
            };

第一次InvokeScript时候用eval函数执行我们在代码中写好的JS脚本,将需要的方法attach到window节点上。而第二次InvokeScript直接执行需要的方法,为每一个<a>绑定点击事件处理函数。

运行后达到了我们的预期效果。对于ScriptNotify而言,在Mango版本前,从外部通过eval注入的方法调用window.external.notify()是不好用的。也就是说,只有页面中静态的Javascript才有能力回调code behind。而谢天谢地谢M$,Mango里面终于可以这样做了,省了不少事情。另外这里需要注意的是attachEvent方法所绑定的事件为onmousedown,而不是onclick。

如果希望在Windows Phone 7.0上实现同样的效果,可以走前辈给的指路:在Understanding Web Browser Control of Windows Phone 7 – Part 1一文中的实现方法为注入的Script保留一个window节点上面的全局变量,保存链接后得到的属性。code behind内则有一个每1秒执行一次的Timer去不停地抓取这个属性,发现值不为空情况下则当作用户对链接进行了操作。

从这一点也可以见得别看今天我们费了很大力气才能实现这么个功能,到下一个版本微软一边说“嗟,来用”一边开放了相关接口,实现相同的功能可能就是分分钟的事情了。

接下来只剩下Context Menu的UI展现,参考WP7 ContextMenu in depth | Part1: key concepts and API,下载Silverlight Tookit for Windows Phone,导入程序集,将ContextMenu扔到WebBrowser元素里面:

<phone:WebBrowser x:Name="webBrowser" IsScriptEnabled="False">
    <toolkit:ContextMenuService.ContextMenu>
        <toolkit:ContextMenu x:Name="mnuContext" IsZoomEnabled="False" >
            <toolkit:MenuItem Header="新窗口中打开" Click="MenuItem_Click" />
            <toolkit:MenuItem Header="链接文本" Click="MenuItem_Click_1" />
        </toolkit:ContextMenu>
    </toolkit:ContextMenuService.ContextMenu>
</phone:WebBrowser>

后台绑定好Menu Item的事件,直接拿到在ScriptNotify里面保存的链接信息即可,至此功能完成。看看效果:

实现比较简单,代码就不扔了。题外话,利用InvokeScript还可以做很多事情,例如去掉干扰页面正常打开的target=”_blank”等。同样对于本文的应用而言,可以将不同的链接绑定不同的Context Menu,实现更加丰富的功能。

参考资料

《ちはやふる》(歌牌情缘)

《ちはやふる》这部连名字都不好翻译的今年10月新番看过后,实在是刺激人更新Blog的欲望,以便进行布教。对于获得了2009年漫画大赏以及2010年“这部漫画好厉害!”第一名的同名原作的漫画分类来说,网上的评论多数倾向于“披着少女们漫画皮的热血漫”。老实说是看到翻译有偏差的台版译名《花牌情缘》,作为花牌Fans才决定点进去看一集再说。结果看了半天,发现不是花牌(花札),而是以“小仓百人一首”作为游戏基础的“歌牌”。

虽然花札和歌牌在现代日语中都可以使用来自于葡萄牙语“纸牌”的Carta进行指代,写作“かるた”,但实际上从牌面类型到游戏玩法则是完全不同的。歌牌的另外一个叫法就是“百人一首”,也就是上面说过的“小仓百人一首”。这本是日本镰仓时代的藤原定家收集和编撰的“和歌”集,说白了就是诗集,里面收集了100首从《古今和歌集》等古诗集中挑选的作品。后来到了浮世绘流行的年代,有人将这个集子配上了符合意境的画作,渐渐演变成了纸牌的形式——甚至加入了双人、多人对抗的元素:两方随机分到一半牌面按照自己的喜好排列,听到旁白念出的上半句之后去抢对应的下半句所在的牌面,最终拿到更多牌的人人获胜。因为有的时候抢牌的动作可能非常大,百人一首又有个“榻榻米上的格斗技(畳の上の格闘技)”的称号。

《ちはやふる》的主线推动道具,就是这种双人对抗的古老纸牌游戏。其他动画、漫画作品中也曾有百人一首的出场,例如《幸运星》中大小姐提到自家虽然是偏向西式风格,但在日本新年时候反而以和式方式庆祝,其中就有百人一首的歌牌游戏。另外《寒蝉鸣泣之时》中也有一集用相当的篇幅描绘主角们进行歌牌游戏的场景。

回到本作的剧情上来:女主千早成立的“竞技歌牌部”没人来,但无意中发现小学同学、曾经一起玩百人一首的太一和自己同高中,于是决定拉他入部。故事讲到这里突然拉回到小学时候,出现了另外一个被称为百人一首魔王天才的人物绵谷新。也是拜他所赐,女主终于知道百人一首还可以杀人这么燃,对战起来不啻于简单的听音找牌游戏。对于剧中人物来说,这可能是几十分钟的回忆,但要说清楚来龙去脉又不让观众觉得“啊上来就回忆杀,有没有搞错”,监督实在是太大胆了。

第一集观赏完毕后我在豆瓣上给了3分,觉得不看好剧情发展,对于故事后续展开存疑。结果从第二集开始,这样白痴的评价就被连续打脸:故事不仅充实,而且转折多,避重就轻进行人物描画,女主、太一和新三个人的形象越来越明显,同时在最后三人分离时候煽情了一把的同时还不忘为后续高中时代的故事做铺垫,这个剧的原作、监督、脚本和构成真是不简单!监督浅香守生指导过的作品包括《NANA》、《人间失格》剧场版、《魔卡少女樱》、《青之文学》、《人形电脑天使心》等,可以说偏向于少女漫画改编作品。

而《ちはやふる》的原作末次由紀更要提一下,因为她的经历相当坎坷。虽然09-11年靠着《ちはやふる》拿奖拿到手软,但之前在05年因为旧作《エデンの花》被指控抄袭他人(这个他人名气比较大,井上雄彦……而且竟然抄的是《SLAM DUNK》和《リアル》,末次老师在想什么)作品构图后,因为证据确凿,末次老师承认抄袭并且在杂志刊登谢罪文,正在连载的作品也被暂停,而已发行的单行本全部回收。直到07年才复出进行新作的创作。一年后开始连载《ちはやふる》,可以说是末次老师从少女漫画慢慢转向受众面更大类型漫画的尝试,虽然画风还是标准的少女漫,但在视觉元素下面铺设的却是竞技、热血的故事。所以虽然讲谈社漫画奖上这部作品拿到的还是“少女部门”奖项,但个人认为这仅仅是因为所刊登杂志类型的影响,让人相当期待作者后续作品。在这里想到之前新浪微博上被指认抄袭他人作品的某几位漫画创作者及其支持者的态度,天差地别,虽然最后还是道歉了事,但完全没有为之前的抄袭付出代价。

最后说一说这部作品的音响部分。MadHouse对于第一主角千早大胆起用了新人瀬戸麻沙美,而男性角色方面则选择了已经成名的宮野真守和細谷佳正。而同样在今年因为《那朵花》出名的茅野愛衣也在阵容之内,到目前为止可以说CV表现太好了(或者说没有棒读系就已经很好了)。OP和ED也非常好听,属于看片时候绝对不能跳过的类型。

Update:剧中女主的“神卡”所写的诗句为在原业平所作“ちはやぶる神代もきかず竜田川からくれなゐに水くくるとは”。

很明显,作品名即来源于此,但包括一些专业的百人一首讲解网站以及书籍内都可以看到,这句的开头是“ちはやぶる”,其中第四个假名为浊音,并非作品名中的“ふ”。这个网页内则是两者皆有。之所以如此变化的原因在OP中可以看到,是因为Chihaya Full的谐音,应当表达的是“千早、全力”之意:

顺带一提,这一首的刘德润译本为

悠悠神代事,黯黯不曾问。枫染龙田川,潺潺流水深。

流云字幕组所采用的各集标题译文均应为刘德润版本。