匈牙利命名法

引用一片博文:

匈牙利命名法的衰落和建议

 

首先要说明的是,此文只是一篇关于个人看法的随笔,不是paper。我也无意再次挑起这场旷日持久却已结束的争论。

 

1.不太久远的历史

匈牙利命名法(Hungarian Notation)最初雏形来自Charles Simonyi的一篇论文,后来Charles Simonyi进入Microsoft并担任Microsoft Office开发的要职后得到完善,并在M$内部得到推广,最终演变为当时Windows开发的一种既定编码风格。

Hungarian Notation得到普及另一个人也功不可没(难辞其咎?),Charles Petzold在编写他那本极具影响力的Programming Windows(Windows程序设计)时,将Hungarian Notation作为标准编写了示例代码。而Hungarian Notation也随着这些示例代码影响了好几代Windows上的C程序员。

 

2.应用型和系统性匈牙利

据传Simonyi最初在M$内部推荐使用的命名法称为“应用型匈牙利”。最显著的特点是,它并不表示数据实际类型,而是给出变量所代表的意义

例如:

rw/col 表示行和列
us 表示非安全型字符串,且在使用前需要处理
s 表示安全型字符串

大多数的前缀都来自于自然语言的单词缩写。

但是不巧的是,Simonyi在编写文档时,用了“type”这个单词。其他的工程师在翻阅文档时,误以为type表示的是数据类型。纵使Simonyi在文档中非常详细且准确地揭示了type所代表的意义,一切都为时已晚。

经过错误理解的系统型匈牙利在M$内部飞速流传开来,Simonyi也意识到自己对此已经无能为力,最终也只能将错就错。

ps:以上故事来自Joel编写的More Joel On Software(软件随想录)

于是,Hungarian Notation就变成了现在这个样子,每个名字前都用前缀来表示数据类型。

例如:

int -> n
long -> l
char -> ch
unsigned * -> u
pointer -> p
char*/wchar_t* -> psz
char[]/wchar_t[] -> sz
HANDLE -> h
DWORD -> dw
….

系统型匈牙利的好处是,你可以一眼从变量名中获取变量的类型,这在IDE尚不是很强大的时代具有一定的作用。

于是,慢慢地,Hungarian Notation开始占据Windows开发的主流风格,影响了一代又一代的C/Windows程序员

 

3.(系统型)匈牙利命名的缺点

Hungarian Notation最大的缺点是:(现在而言)几乎没有优点

虽然可以一眼从变量名中获取类型,但是在实际开发中,强大的IDE可以马上显示变量的具体定义信息,这使得命名法的作用大大降低。

更恐怖的是,进入C++之后,随着数据类型的增加和template的出现,使得为变量加入类型前缀变得不靠谱。

例如:

如果有一个变量的类型是 std::map<std::vector<std::wstring>, std::vector<std::wstring> >,那么确定它的类型前缀是一件很痛苦的事情,其程度不亚于你要陪你的GF/WIFE去逛街挑选衣服。

在猛烈的抨击和强烈的抗议下,M$终于在进入.NET大潮时发表宣言,表示不赞成继续使用Hungarian Notation

除了Word和Excel开发团队外(当年Simonyi直接负责的团队,也只有他们才知道事情的个中缘由),几乎所有人都感到如释重负,因为他们终于可以不用再理会这种操蛋的命名了。

 

4.个人观点

作为一个在Windows上用C/C++写程序,且受到Charles Petzold熏陶的程序员来说,使用Hungarian Notation为变量命名几乎成为了一种本能。

第一次反思是否应该摒弃这种命名法是在高三阅读Steve McConnell那本Code Complete之时,有意思的是,McConnell也曾经在M$工作过一段时间。

第二次反思是在不久前阅读Joel的More Joel On Software之时,并且这次反思最终促使我写下这篇文章,表达一些极具个人角度的观点。

我的观点很简单(我把它称作最小化原则的匈牙利命名法),分为两部分:

(1)控件的命名依旧采用Hungarian Notation,例如:

Button -> btn
Label -> lbl
TextBox -> txt

控件上的Hungarian Notation要靠谱的多

(2)变量命名上,仅保留以下前缀

global -> g_
member -> m_
static -> s_
pointer -> p
char*/wchar_t* -> psz
char[]/wchar_t[] ->sz

m_能够与非成员变量区分,而且基本可以避免变量重名而需要使用this指针的情况

g_和s_的意图无需多说,臭名昭著的global和static从来都需要特别对待

有人可能会对剩下的三个前缀颇有微词,我的理由是,这三个类型(其实只有两个)实在太特殊而且需要引起足够的注意,加前缀的意图则是告诉你:be careful! be careful!be careful!

其他的类型都尽量不加前缀。

 

5.两种风格的代码

为了对比效果,我特意临时写了一段新版本的代码,将两个版本进行对比。

代码则是MergeSort排序算法

不过要说一点,新版本不是在原版本基础上改写的,而是重新写的,所以某些代码会稍稍不同

老版本,采用近乎标准的匈牙利命名法

新版本,最小化匈牙利命名法

 

void MergeSort(int a[], int low, int high)
{
    if (low < high)
    {
        int midIndex = (low + high) >> 1;
        // take apart
        MergeSort(a, low, midIndex);
        MergeSort(a, midIndex + 1, high);
        
        Merge(a, low, midIndex + 1, high);
    }
}
 
 
void Merge(int a[], int lowFirst, int highFirst, int highLast)
{
    int l = lowFirst;
    int r = highFirst;
    int len = highLast - lowFirst + 1;
    
    int* pBuffer = new int[len];
    _ASSERT(pBuffer != NULL);
    int* p = pBuffer;
    
    while (l < highFirst && r <= highLast)
    {
        *p++ = (a[l] <= a[r]) ? a[l++] : a[r++];
    }
    
    while (l < highFirst)
    {
        *p++ = a[l++];
    }
    
    while (r <= highLast)
    {
        *p++ = a[r++];
    }    
    
    for (int i = lowFirst, p = pBuffer; i <= highLast;)
    {
        a[i++] = *p++;
    }
    
    delete [] pBuffer;
}

 

posted on 2014-01-20 19:43  刘宝成  阅读(647)  评论(0编辑  收藏  举报

导航