惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
Darknet – Hacking Tools, Hacker News & Cyber Security
Jina AI
Jina AI
博客园_首页
J
Java Code Geeks
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 司徒正美
Hugging Face - Blog
Hugging Face - Blog
S
SegmentFault 最新的问题
MyScale Blog
MyScale Blog
P
Proofpoint News Feed
L
Lohrmann on Cybersecurity
Forbes - Security
Forbes - Security
大猫的无限游戏
大猫的无限游戏
Vercel News
Vercel News
Y
Y Combinator Blog
Google DeepMind News
Google DeepMind News
The Register - Security
The Register - Security
N
News | PayPal Newsroom
S
Security Archives - TechRepublic
量子位
Cisco Talos Blog
Cisco Talos Blog
V
V2EX
C
Cisco Blogs
The Cloudflare Blog
Stack Overflow Blog
Stack Overflow Blog
L
LangChain Blog
Scott Helme
Scott Helme
S
Securelist
Security Latest
Security Latest
爱范儿
爱范儿
TaoSecurity Blog
TaoSecurity Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
I
Intezer
L
LINUX DO - 最新话题
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
C
Check Point Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
美团技术团队
Know Your Adversary
Know Your Adversary
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
PCI Perspectives
PCI Perspectives
月光博客
月光博客
T
Tailwind CSS Blog
Cloudbric
Cloudbric
小众软件
小众软件
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
K
Kaspersky official blog
D
DataBreaches.Net
博客园 - 【当耐特】
有赞技术团队
有赞技术团队

博客园 - duguguiyu

『Android开发精要』推荐 深入Android【八】 —— Activity间数据传输 深入Android【七】 —— 资源文件 深入Android 【六】 —— 界面构造 深入Android 【五】 —— 任务和进程 深入Android 【四】 —— 组件调用 深入Android 【三】 —— 组件入门 深入Android 【二】 —— 架构和学习 深入Android 【一】 —— 序及开篇 Symbian手记【五】 —— Symbian的异步框架 Symbian手记【四】 —— Symbian的容器 Symbian手记【三】 —— Symbian的描述符 Symbian手记【二】 —— Symbian对象构造 分布式基础学习【二】 —— 分布式计算系统(Map/Reduce) 分布式基础学习【一】 —— 分布式文件系统 Chrome源码剖析 【五】 Chrome源码剖析 【四】 Chrome源码剖析【三】 Chrome源码剖析【二】
Symbian手记【一】 —— Symbian命名法
duguguiyu · 2009-05-25 · via 博客园 - duguguiyu

【一】 Symbian命名法

每个美感尚存的C++ coder,第一次看到Symbian C++的程序,第一反应是:这鬼代码怎么缩进的?接下来,所有人会有疑问应该是:函数和类上的乱七八糟的前后缀是啥意思?

娃再丑也是爸妈生的,生成这模样虽然很无奈,但确实也是事出有因。在我看来,Symbian命名法的核心出发点,就是为了更好的内存资源管理。C++的人肉内存管理模式,在给人以控制到字节的快感的同时,也带了了麻烦到每行代码的烦恼。命名法,就是Symbian设计者憋出来用来辅助管理内存资源的方式之一。

类命名

Symbian的类,通常都带着一个字母的前缀,比如C、M、T、R、H等等。

所有从CBase派生而来的子类,都以C开头,形如Cxxxx。每个正确设计的,非抽象(不可实例化)的C类,都只能在堆上分配。为了保证这一点,每一个可实例化的C类,都应该按照Symbian的二阶段构造模式。但当然,这可以有意外。比如一些派生自CCoeControl的控件对象类,会需要从Resource文件中构造类的成员对象(而不仅仅通过二阶段中的ConstructL方式来构造),这使得它可能不适合按照二阶段构造的方式来封装。

做过.Net或者Java的人应该都明白,保持一个单一根的类型系统有什么好处,.Net在没有泛型的日子里,就是通过这个共根来实现一些基础的容器和方法。但这个好处,在C++,尤其是Symbian C++中体现的并不明显。因为C++有void *(在Symbian中华丽的转身为TAny *),有模板,可以来做一些类似的事情。更重要的,在Symbian C++中,为了节约空间,把虚表的RTTI项给精简掉了,使得Symbian C++的类丧失了dynamic_cast的能力,从而导致整个Symbian在运行期的动态识别能力,很是孱弱。

所以说,之所以要从CBase类进行派生堆上对象,很重要的一个原因,就是为了内存管理。CBase做了一件很重要的事情,就是将拷贝构造函数和赋值函数设置成了私有。这意味着,所有从CBase派生的子类,都默认被阉割了一刀,失去了拷贝构造的能力。这是为了提醒所有使用者,C对象的浅拷贝是不受欢迎的,如果你想提供该对象的拷贝功能(要深拷贝,不要浅...),往往是利用一些CloneL之类的接口来实现,保证行为的统一性。

C的类们,都涌向了堆中,栈上的活,留给了T类来完成。T类没有什么特殊的继承结构,每个T类,需要可以随意的在堆上或者栈上分配。大部分时候,它们该待的地方是栈,在栈上分配,并可以快速拷贝,一旦被析构,所以资源被释放,生不带来死不带走不留下一点残渣。因此,它们不应该包含大块的数据对象,但却可以拥有很复杂的接口,增加操作的便利性。比如,TRgb、TRect之类的系统类,就是典型的人小鬼大的代表人物。但T类不是C++的old plain类,它可以有继承结构,比如Symbian中描述符的那一堆堆T类,就拥有复杂的继承结构。

但世界是残酷的,有的类,偏偏就是投错了胎,搞得人不人,鬼不鬼。HBuf,就是此中代表。为了保持队形,维系接口,HBuf派生自TDesC,用以表示分配在堆上的Symbian描述符(就是字符串...)。但与一般T类不同,因为其占用空间动态变化,它必须在堆上分配,所以丧失了叫T的权利;另一方面,为了接口,它派生了T类,在排斥多根的C++中,它就不能够在从CBase派生了(继承的局限性,可见一斑),被断了叫C的后路。于是,就带上了H的特殊帽子,表示其在堆上分配,但不苟且于C类的屋檐下。

R类,换成通俗的描述,就是句柄类。它天生为了管理资源而存活,R类本身很简单,通常在栈上分配,可以拷贝,在这一点非常接近于T类。但与T类不同的时,R类往往带有某个堆对象的指针,指向文件之类的资源,或者是大块的堆数据对象。它析构的时候,默认是不析构这个指向的对象的,而是提供了一些类似于Close,Release之类的接口,需要人肉手动释放。有的T类也是指向另一块堆或者栈区域的,比如TPtr类。这两者一个本质的区别在于,T类指向的对象,不是它自己分配,它只是提供一个快捷方式,并不管指向对象的死活;而R类指向的资源,往往是自己本身或者另一个同类分配的资源,R类对象指向的资源,必须从这个R类的对象构造,从这个R类的对象析构(两个对象可以不同,但类是一致的)。

在Symbian C++中,还有一些类,不涉及任何内存资源。一个就是接口类,它们以M开头,相当于.Net的Interface,是一个纯虚类。每个Symbian中的类,可以派生自若干个M类,但仅仅能从一个有内存资源的对象那里进行分配。理论上,作为一个纯虚类,应该提供一个虚的析构函数,但在Symbian C++中,这往往是不需要的。因为在一个没有RTTI的世界里,只有第一个被继承的接口才有可能成功析构所有对象。比如一个类,形如 class A : public Cxxx, public Mxxx。只有用Cxxx接口才能管理资源,对Mxxx接口进行delete,完全没有办法释放全部资源(除非Cxxx里面没有任何数据...)。而Symbian的堆对象往往派生自CBase,所以,不可能从一个M类来析构对象,这个析构函数成不成虚,就是无关紧要的事情了。

另外一个不含任何资源的类,就是静态类了,在Symbian C++中它们没有任何前缀,是唯一不戴帽子的家伙。这个和.Net的static class一样,只包含一堆的静态方法,需要屏蔽所有构造、析构、拷贝接口(要没有这个闲工夫,不屏蔽也无所谓了...)。虽然,C++有函数,但出于对面向对象的热衷,使用这样的静态类,还是很值得鼓励的。。。

函数命名

在Symbian中,类是戴帽子的,函数则是穿裤子的。在Symbian的函数(包括成员和非成员函数)中,常有两种后缀,一个是L,另一个是LC。L,就是告诉你,这个函数可能Leave,换人类可知的语言描述,就是这个函数会抛出异常,需要谨慎处理。L是有传递性的,如果在调用该函数的地方对此L不理不睬放任其Leave,那么,在此调用函数后面,也需要添加一个L。

除了L,还有跟进一步的LC。这通常都是构造性的函数,它告诉你,它构造的过程中,不但可能Leave,并且分配的对象处于清理栈中。这是一个接近于语法糖的功能,如果在本函数中的后续部分需要调用被构造对象的相关接口,应该用LC,然后自己pop,而不是L。

其他命名法

还有一些对象,是会被带着前缀的。比如对象的成员变量,都带着前缀i;函数参数,都带着a(如果后面是原因字母开头,则需要用an,*_*)。在成员变量加前缀,这是常用的手段,可以和成员变量区分开了,帮助节约命名一个变量的脑细胞。但对函数形参加前缀,就是一件很诡异的事情了,剖有画蛇添足的艺术气息。

在Symbian中,所有的常量,都应该是K开头的,包括定义的const量,_LIT定义的字符常量等等。而枚举类型,同属于T类型,以T开头,其中的枚举值,则是以E开头。给这些类型的东西建立命名法,是常见的手段,只是Symbian不走寻常路,命名方式上不屑于与别人苟同。。。

结语

简而言之,Symbian制定了一套复杂的命名法规则,期待以此来规范化内存管理等操作。但世界的残酷在于,一个没有强制的标准,是不可靠的。命名法是一种弱约束的东西,工期赶的再急,也不可能无视编译和运行时的错误,但却可以无条件的忽视命名规则。并且,命名法是有强烈的破窗效应,一旦某一个函数没有合理的添加L,所有直接和间接调用它的函数,都可能会错误使用它,从而埋下隐患。况且,Symbian的命名法也算是枝繁叶茂了,很容易让人看不清楚端倪,不知不觉的就用错了,一个团队每个人在这上面犯一些错误,到最后命名法就完全丧失了效能。不过,就算是环境恶劣,对于个人而言,还是应该严于律己的,不论如何,不要轻易抛弃正确命名,这样,才可能造福大家。。。