为什么在 C + + 类中对成员变量使用前缀

许多 C + + 代码使用语法约定来标记成员变量

  • M _ 成员名称用于公共成员(其中完全使用公共成员)
  • _ 成员名称用于私有成员或所有成员

其他人则尝试在使用成员变量时强制使用 this-> 成员

根据我的经验,大多数较大的代码库无法始终如一地应用这些规则。

在其他语言中,这些约定远没有那么普遍。我只是偶尔在 Java 或 C # 代码中看到它。我想我从来没有在 Ruby 或 Python 代码中看到过它。因此,现代语言中似乎有一种趋势,即不对成员变量使用特殊的标记。

这个约定在今天的 C + + 中仍然有用吗? 或者它只是一个时代错误。特别是因为它在各个库之间的使用非常不一致。难道其他语言没有表明可以不使用成员前缀吗?

134913 次浏览

其他人则试图强制使用 此-> 成员每当成员 使用变量

通常是 因为没有前缀。编译器需要足够的信息来解析有问题的变量,无论是因为前缀而使用的唯一名称,还是通过 this关键字。

所以,是的,我认为前缀仍然有用。比如,我更喜欢输入“ _”来访问成员,而不是输入“ this->”。

我不能说它有多普遍,但就我个人而言,我总是(并且一直)在我的成员变量前面加上‘ m’。例如:

class Person {
....
private:
std::string mName;
};

这是我使用的唯一一种前缀形式(我非常反对使用匈牙利命名法) ,但这些年来它一直对我很有帮助。顺便说一句,我通常不喜欢在名称(或其他任何地方)中使用下划线,但预处理器宏名称例外,因为它们通常都是大写。

那些会议就是这样。大多数商店使用代码约定来简化代码的可读性,因此任何人都可以轻松地查看一段代码,并快速地破译公共成员和私有成员之间的信息。

当您有一个大的方法或代码块时,立即知道是使用局部变量还是成员是很方便的。这是为了避免错误和更好的清晰度!

我不认为一种语法比另一种语法有实际价值。正如您所提到的,所有这些都归结为整个源文件的一致性。

我发现这些规则唯一有趣的地方是当我需要两个同名的东西时,例如:

void myFunc(int index){
this->index = index;
}


void myFunc(int index){
m_index = index;
}

我用它来区分两者。另外,当我包装调用时,比如来自 windows Dll 的调用,来自 Dll 的 RecvPacket (...)可能会包装在我的代码中的 RecvPacket (...)中。在这些特殊情况下,使用像“ _”这样的前缀可能会使两者看起来很相似,很容易区分哪一个是哪一个,但是对于编译器来说是不同的

使用前导下划线时必须小心。保留单词中大写字母前的前导下划线。 例如:

是的

都是保留词

(咒语)

我..

不是。还有一些情况下,不允许在小写字母之前加上前导下划线。在我的具体案例中,我发现 _ L 碰巧被 Visual C + + 2005保留,这种冲突产生了一些意想不到的结果。

我对标记局部变量是多么有用持观望态度。

这里有一个关于保留哪些标识符的链接: 在 C + + 标识符中使用下划线的规则是什么?

其他语言将使用编码约定,他们只是倾向于不同。例如,C # 可能有两种不同的风格,一种是 C + + 方法(_ variable、 mVariable 或其他前缀,比如匈牙利命名法) ,另一种是我称之为 StyleCop 的方法。

private int privateMember;
public int PublicMember;


public int Function(int parameter)
{
// StyleCop enforces using this. for class members.
this.privateMember = parameter;
}

最后,它变成了人们所知道的和看起来最好的。我个人认为代码在没有匈牙利命名法的情况下更容易阅读,但是如果附加了匈牙利命名法,找到带智能感知的变量就会变得更容易。

在我上面的示例中,您不需要成员变量的 m 前缀,因为您的用法前面加上了这个。在编译器强制的方法中指示同样的事情。

这并不一定意味着其他方法是坏的,人们坚持什么工作。

我的天,这是私人恩怨。我一个前缀都没加。无论如何,如果代码是公共的,我认为它应该更好地有一些前缀,这样它可以更具可读性。

大公司经常使用自己的所谓“开发者规则”。
顺便说一句,我见过的最有趣也是最聪明的是干吻(不要重复自己。保持简单,愚蠢) : -)

我认为,如果需要前缀来区分类成员与成员函数参数和局部变量,那么要么函数太大,要么变量的名称不好。如果它不适合在屏幕上,所以你可以很容易地看到什么是什么,重构。

考虑到它们通常在远离使用它们的地方声明,我发现全局常量(和全局变量,尽管 IMO 很少需要使用它们)的命名约定是有意义的。但除此之外,我觉得没什么必要。

也就是说,我过去常常在所有私有类成员的末尾放置下划线。由于我的所有数据都是私有的,这意味着成员有一个尾随下划线。我通常不再在新的代码库中这样做,但是因为作为一个程序员,您大多数使用旧的代码,所以我仍然经常这样做。我不确定我对这个习惯的容忍是因为我过去总是这样做,现在仍然经常这样做,还是因为它真的比标记成员变量更有意义。

正如其他人已经说过的,重要的是要口语化(将命名样式和约定适应于您正在编写的代码基)并保持一致。

多年来,我一直致力于开发一个大型代码库,该代码库既使用“ this->”约定,也使用 后缀下划线表示成员变量。多年来,我也参与过一些小型项目,其中一些没有任何命名成员变量的约定,另一些则有不同的命名成员变量的约定。在那些较小的项目中,我一直发现那些缺乏任何惯例的项目是最难迅速投入和理解的。

我对命名很敏感。我会为一个类或变量的名字而苦恼,以至于,如果我不能想出一些我觉得“好”的东西,我会选择给它起一些毫无意义的名字,然后提供一个注释来描述它的真实面目。那样的话,至少这个名字的意思和我想要表达的完全一样——不多也不少。通常,在使用它一段时间后,我会发现 真的应该是什么名字,然后可以返回并适当地修改或重构它。

关于 IDE 完成这项工作的最后一点——这非常好,但是在我执行最紧急工作的环境中,IDE 通常是不可用的。有时,此时唯一可用的就是“ vi”的副本。此外,我还看到过许多 IDE 代码完成导致愚蠢行为的案例,例如名称中的错误拼写。因此,我倾向于不依赖 IDE 拐杖。

由于内存管理,区分成员变量和局部变量很有用。一般来说,堆分配的成员变量应该在析构函数中销毁,而堆分配的本地变量应该在该范围内销毁。对成员变量应用变数命名原则有助于正确的内存管理。

C + + 成员变量前缀的最初想法是存储编译器不知道的其他类型信息。例如,可以有一个字符串是固定长度的字符,另一个字符串是变量,以’0’结尾。对于编译器来说,它们都是 char *,但是如果你试图从一个文件复制到另一个文件,你就会遇到很大的麻烦。所以,我第一时间想到,

Char * aszFred = “嗨,我是个空终止字符串”;
Char * arrWilma = {‘ O’,‘ o’,‘ p’,‘ s’} ;

其中“ asz”表示这个变量是“ ascii 字符串(以零结尾)”,“ arr”表示这个变量是一个字符数组。

然后奇迹发生了,编译器会非常满意这句话:

Strcpy (arrWilma,aszFred) ;

但是你,作为一个人,可以看着它说“嘿,这些变量不是真正的同一类型,我不能这样做”。

不幸的是,很多地方都使用标准,比如成员变量使用“ m _”,整数使用“ i”,字符指针使用“ cp”。换句话说,它们是在复制编译器所知道的内容,同时使代码难以阅读。我认为这种有害的做法应该被法律禁止,并受到严厉的惩罚。

最后,我要提到两点:

  • 明智地使用 C + + 特性可以让编译器知道必须用原始的 C 样式变量编码的信息。可以创建只允许有效操作的类。这应该尽可能实际地去做。
  • 如果代码块太长,以至于在使用变量之前忘记了变量的类型,那么它们就是 方式太长。不要用名字,重新组织。

我更喜欢后缀下划线,比如:

class Foo
{
private:
int bar_;


public:
int bar() { return bar_; }
};

我通常不为成员变量使用前缀。

我过去常常使用 m前缀,直到有人指出“ C + + 已经有一个用于成员访问的标准前缀: this->

这就是我现在用的。也就是说,当有模棱两可的时候,我添加了 this->前缀,但通常不存在歧义,我可以直接引用变量名。

对我来说,这是两全其美的。我有一个前缀,我可以使用,当我需要它,我可以自由离开它,只要有可能。

当然,显而易见的反对意见是“是的,但是你不能一眼看出变量是否是类成员”。

我说“那又怎样?”?如果您需要知道这一点,那么您的类可能有太多的状态。或者函数过于庞大和复杂。”。

在实践中,我发现这种方法非常有效。作为额外的好处,它允许我轻松地将一个局部变量提升为类成员(或者反过来) ,而不必重命名它。

最棒的是,它始终如一!我不需要做任何特别的事情或者记住任何约定来保持一致性。


顺便说一下,你的 不应该为你的类成员使用前导下划线。您会不舒服地接近实现保留的名称。

标准保留所有以双下划线或大写字母跟随的下划线开头的名称。它还保留以单个下划线 在全局命名空间中开头的所有名称。

因此,类成员使用前导下划线后跟小写字母是合法的,但迟早你会对以大写字母开头的标识符做同样的事情,否则就会违反上述规则之一。

所以避免使用前导下划线更容易。如果希望在变量名中编码作用域,请使用后缀下划线或 m_或仅使用 m前缀。

我们的项目总是使用“ its”作为成员数据的前缀,使用“ the”作为参数的前缀,没有局部变量的前缀。这有点矫揉造作,但是它被我们系统的早期开发人员采用了,因为他们认为它被我们当时使用的一些商业源代码库(XVT 或 RogueWave ——也许两者都有)作为惯例使用。你会得到这样的结果:

void
MyClass::SetName(const RWCString &theName)
{
itsName = theName;
}

我认为确定前缀范围的最大原因(没有其他原因——我讨厌匈牙利命名法)是,它可以防止你在编写代码时遇到麻烦,因为你以为你引用的是一个变量,但实际上你引用的是另一个在本地范围内定义的同名变量。它还避免了使用变量名来表示相同的概念,但使用不同的作用域的问题,如上面的示例所示。在这种情况下,无论如何您都必须为参数“ theName”想出一些前缀或不同的名称——为什么不制定一个适用于所有地方的一致规则呢。

仅仅使用这个-> 还不够好-我们对减少模糊性的兴趣不如对减少编码错误的兴趣,并且使用局部作用域标识符掩盖名称可能是一件痛苦的事情。当然,一些编译器可以选择对在更大范围内隐藏名称的情况提出警告,但是如果您使用的是大量的第三方库,而这些第三方库恰好为未使用的变量选择了名称,这些名称偶尔会与您自己的名称发生冲突,那么这些警告可能会成为一个麻烦。

至于它/它本身——老实说,我发现它比下划线更容易输入(作为一个触摸式打字员,我尽可能避免使用下划线——过多地延伸主行) ,而且我发现它比神秘的下划线更易读。

我之所以使用它,是因为 VC + + 的智能感知无法判断何时显示私有成员。唯一的指示是 Intellisense 列表中字段图标上的一个小“锁”符号。它只是使识别私有成员(字段)变得更加容易。老实说,这也是 C # 的一个习惯。

class Person {
std::string m_Name;
public:
std::string Name() { return m_Name; }
void SetName(std::string name) { m_Name = name; }
};


int main() {
Person *p = new Person();
p->Name(); // valid
p->m_Name; // invalid, compiler throws error. but intellisense doesn't know this..
return 1;
}

CodeComplete 建议成员变量使用 m _ varname。

虽然我从来不认为 m _ 符号有用,但是在构建标准时,我会考虑 McConnell 的意见。

一些响应关注重构,而不是命名约定,作为提高可读性的方法。我不认为一个可以取代另一个。

我知道有些程序员不喜欢使用局部声明; 他们喜欢将所有声明放在一个块的顶部(如 C 语言) ,这样他们就知道在哪里可以找到它们。我发现,在作用域允许的情况下,在首次使用变量的地方声明变量,可以减少我向后查找声明的时间。(对我来说,即使是小函数也是如此。)这让我更容易理解我正在看的代码。

我希望这与成员命名约定之间的关系足够清楚: 当成员前缀一致时,我根本不必回头查看; 我知道声明甚至不会出现在源文件中。

我肯定我一开始并不喜欢这些款式。然而,随着时间的推移,在一贯使用它们的环境中工作,我优化了自己的思维,以利用它们的优势。我认为有可能许多现在对它们感到不舒服的人也会更喜欢它们,因为它们的用法是一致的。

我全力支持 前缀做得很好

我认为(系统)匈牙利命名法对大多数前缀得到的“坏名声”负有责任。

这种表示法在强类型语言中很大程度上是没有意义的,例如在 C + + “ lpsz”中,它告诉你你的字符串是一个指向空结束字符串的长指针,当: 分段体系结构是古老的历史,C + + 字符串是通常约定的指向空结束字符数组的指针,并且知道“ customerName”是一个字符串并不是真的那么困难!

然而,我确实使用前缀来指定变量的 用途(本质上是“ Apps 匈牙利语”,尽管我更喜欢避免使用匈牙利语,因为它与匈牙利语系统有着糟糕而不公平的联系) ,这是一种非常方便的 节省时间减少虫害方法。

我用:

  • M 为会员
  • C 表示常量/readonly
  • P 表示指针(pp 表示指针到指针)
  • V 代表不稳定
  • 是静电干扰
  • I 表示索引和迭代器
  • 事件

如果我希望使 类型清晰,我会使用标准的后缀(例如 List、 ComboBox 等)。

这使得程序员在看到/使用变量时能够意识到它的 用途。可以说,最重要的情况是“ p”表示指针(因为用法从 var 改变)。指向 var-> ,并且您必须更加小心地使用指针-NULL、指针算法等) ,但是其他所有指针都非常方便。

例如,您可以在一个函数中以多种方式使用相同的变量名: (这里是一个 C + + 示例,但它同样适用于多种语言)

MyClass::MyClass(int numItems)
{
mNumItems = numItems;
for (int iItem = 0; iItem < mNumItems; iItem++)
{
Item *pItem = new Item();
itemList[iItem] = pItem;
}
}

你可以在这里看到:

  • 成员和参数之间没有混淆
  • 索引/迭代器和项之间没有混淆
  • 使用一组清晰相关的变量(项目列表、指针和索引) ,以避免“ count”、“ index”等通用(模糊)名称的许多缺陷。
  • 前缀比“ itemIndex”和“ itemPtr”等替代词减少了键入(更短,并且在自动完成时效果更好)

“ iName”迭代器的另一个伟大之处在于,我从不用错误的索引来索引一个数组,如果我在另一个循环中复制一个循环,我就不必重构一个循环索引变量。

比较一下这个不切实际的简单例子:

for (int i = 0; i < 100; i++)
for (int j = 0; j < 5; j++)
list[i].score += other[j].score;

(这很难理解,而且经常导致在用“ j”的地方使用“ i”)

与:

for (int iCompany = 0; iCompany < numCompanies; iCompany++)
for (int iUser = 0; iUser < numUsers; iUser++)
companyList[iCompany].score += userList[iUser].score;

(这样更具可读性,并且消除了所有关于索引的混淆。使用现代 IDE 中的自动完成功能,这也是快速且易于输入的)

下一个好处是可以理解代码段 不需要任何上下文。我可以将两行代码复制到一封电子邮件或一个文档中,任何阅读该代码片段的人都可以区分所有成员、常量、指针、索引等。我不需要加上“哦,要小心,因为‘ data’是一个指向指针的指针”,因为它叫做‘ ppData’。

出于同样的原因,我不必为了理解它而把眼睛从一行代码中移开。我不必搜索代码来查找“ data”是否是局部的、参数的、成员的或常量的。我不必把手移到鼠标上,这样我就可以把鼠标悬停在“ data”上,然后等待弹出一个工具提示(有时从来不会出现)。因此,程序员可以更快地阅读和理解代码 意义重大,因为他们不会浪费时间上下搜索或等待。

(如果你不认为你浪费时间上下搜索来解决问题,找到一些你一年前写的代码,而且还没有看过 打开文件,跳到一半而不读它。 看看你能从这一点读多远,在你不知道是否 某物是一个成员,参数或局部。现在跳转到另一个随机 这是我们单身时一整天都在做的事情 通过别人的代码或试图理解如何 调用它们的函数)

“ m”前缀还避免了(恕我直言)丑陋和冗长的“ this->”表示法,以及它所保证的不一致性(即使你很小心,通常也会在同一个类中结束“ this-> data”和“ data”的混合,因为没有什么强制名称的一致拼写)。

‘ this’符号是用来解析 模棱两可的——但是为什么有人会故意编写模棱两可的代码呢?模糊 威尔迟早会导致 bug。在某些语言中,“ this”不能用于静态成员,因此必须在编码风格中引入“特殊情况”。我更喜欢有一个简单的编码规则,适用于所有地方-明确的,明确的和一致的。

最后一个主要好处是智能感知和自动完成。尝试在 Windows 窗体上使用 Intellisense 来查找事件——您必须滚动数百个神秘的基类方法,这些方法永远不需要调用来查找事件。但是,如果每个事件都有一个“ e”前缀,那么它们将自动被列在“ e”下面的一个组中。因此,前缀可以对智能感知列表中的成员、常量、事件等进行分组,从而更快、更容易地找到所需的名称。(通常,一个方法可能有大约20-50个值(局部变量、参数、成员、常量、事件) ,可以在其作用域内访问。但是在输入前缀之后(我现在想使用一个索引,所以我输入‘ i。.’) ,我只有2-5个自动完成的选项。前缀和有意义的名字是人们“额外输入”的原因,这大大减少了搜索空间,显著加快了开发速度)

我是一个懒惰的程序员,上面的约定为我节省了很多工作。我可以编码更快,犯的错误也少得多,因为我知道应该如何使用每个变量。


反对

那么,缺点是什么呢? 反对前缀的典型论据是:

  • “前缀方案是坏的/邪恶的”。我同意“ m _ lpsz”及其类似的词是考虑不周和完全无用的。这就是为什么我建议使用设计良好的符号来支持您的需求,而不是复制不适合您的上下文的内容。(使用正确的工具完成工作)。

  • “如果我改变了某事物的用法,我必须重新命名它” 。是的,当然需要,这就是重构的意义所在,也是为什么 IDE 拥有重构工具来快速、轻松地完成这项工作的原因。即使没有前缀,改变变量的用法几乎肯定意味着要改变它的名称 应该

  • “前缀只会让我迷惑”。每个工具都是如此,直到你学会如何使用它。一旦你的大脑习惯了命名模式,它就会自动过滤掉这些信息,你就不会再介意前缀的存在了。但是你必须在一两个星期内坚定地使用这样的方案,才能真正变得“流利”。这时,许多人看着旧代码,开始想知道他们是如何管理 没有一个好的前缀方案的。

  • “我只要看看代码就可以解决这个问题” 。是的,但是你不需要浪费时间在代码的其他地方寻找或者记住它的每一个小细节,当答案就在你的眼睛已经关注的地方。

  • 只要等待变量 上弹出一个工具提示,就可以找到其中的一些信息。是的。在支持的情况下,对于某些类型的前缀,当您的代码编译干净时,经过一段时间的等待,您可以通读一段描述,并找到前缀可以立即传达的信息。我认为前缀是一种更简单、更可靠和更有效的方法。

  • “更多的是打字”。真的吗?多一个角色?或者说,使用 IDE 自动完成工具,通常会减少输入,因为每个前缀字符都会显著缩小搜索空间。按下“ e”,你的类中的三个事件就会以智能方式弹出。按“ c”,列出了五个常量。

  • “我可以用 this->代替 m 。不,你可以的。但那只是一个更加丑陋和冗长的前缀!只是它带有更大的风险(特别是在团队中) ,因为对编译器来说它是 可以选择,因此它的使用经常是不一致的。另一方面,m简洁、清晰、明确,而且不是可选的,所以使用它更难犯错误。

我几乎从不在变量名前面使用前缀。如果您正在使用一个足够好的 IDE,那么您应该能够轻松地重构和查找引用。我使用非常清晰的名称,并且不害怕使用长的变量名。我也从来没有在范围上遇到过这种哲学上的麻烦。

我唯一使用前缀的时候是在签名行上。我将在参数前面加上 _ 方法的前缀,这样我就可以围绕它们进行防御性编程。

使用成员前缀的主要原因是为了区分成员函数和具有相同名称的成员变量。如果将 getter 与事物的名称一起使用,这非常有用。

考虑一下:

class person
{
public:
person(const std::string& full_name)
: full_name_(full_name)
{}


const std::string& full_name() const { return full_name_; }
private:
std::string full_name_;
};

在这种情况下,成员变量不能命名为 full_name。您需要将成员函数重命名为 get_full_name(),或者以某种方式修饰成员变量。

最近我倾向于使用 m _ prefix 而不是没有前缀,原因并不是因为标记成员变量很重要,而是因为它避免了歧义,比如你有这样的代码:

void set_foo(int foo) { foo = foo; }

这个原因不起作用,只允许一个 foo。所以你的选择是:

  • this->foo = foo;

    我不喜欢它,因为它导致参数阴影,你不再能使用 g++ -Wshadow警告,它的类型也比 m_更长。当使用 int foo;int foo();时,仍然会遇到变量和函数之间的命名冲突。

  • foo = foo_;foo = arg_foo;

    我已经使用它一段时间了,但是它使得参数列表很难看,文档不应该在实现中处理名称不明确的问题。此处还存在变量和函数之间的命名冲突。

  • m_foo = foo;

    API 文档保持干净,你不会得到成员函数和变量之间的歧义,它的类型比 this->短。唯一的缺点是它使 POD 结构变得难看,但是由于 POD 结构首先不会受到名称歧义的影响,因此不需要与它们一起使用。有一个唯一的前缀也使一些搜索和替换操作更容易。

  • foo_ = foo;

    m_的大多数优点都适用,但是我出于审美原因拒绝它,拖尾或前导下划线只会使变量看起来不完整和不平衡。m_看起来好多了。使用 m_也更具可扩展性,因为您可以使用 g_进行全局处理,使用 s_进行静态处理。

PS: 之所以在 Python 或 Ruby 中看不到 m_,是因为这两种语言都强制使用自己的前缀,Ruby 对成员变量使用 @,而 Python 需要 self.

在 python 中,前导双下划线用于模拟私有成员。有关详细信息,请参阅 这个答案

当通读一个成员函数时,知道谁“拥有”每个变量对于理解变量的含义是绝对必要的。在这样的函数中:

void Foo::bar( int apples )
{
int bananas = apples + grapes;
melons = grapes * bananas;
spuds += melons;
}

... 很容易看出苹果和香蕉是从哪里来的,但是葡萄,甜瓜和土豆呢?我们应该查看全局名称空间吗?在班级声明里吗?变量是该对象的成员还是该对象的类的成员?如果不知道这些问题的答案,就无法理解代码。在一个较长的函数中,即使是像苹果和香蕉这样的局部变量的声明也可能在混乱中丢失。

为全局变量、成员变量和静态成员变量(可能分别是 g _、 m _ 和 s _)添加一致的标签可以立即澄清这种情况。

void Foo::bar( int apples )
{
int bananas = apples + g_grapes;
m_melons = g_grapes * bananas;
s_spuds += m_melons;
}

一开始可能需要一些时间来适应这些——但是,编程中有什么是不需要的呢?有一天你甚至觉得{ and }看起来很奇怪。一旦你习惯了它们,它们会帮助你更快地理解代码。

(用“ this->”代替 m _ 是有道理的,但是更加冗长和视觉上的混乱。我不认为它是标记所有成员变量用法的一个好选择。)

对上述参数的一个可能的反对意见是将该参数扩展到类型。了解变量的类型“对于理解变量的含义是绝对必要的”,这也是事实如果是这样,为什么不给每个标识其类型的变量名添加一个前缀呢?按照这个逻辑,你最终会得到匈牙利命名法。但是很多人觉得匈牙利命名法很辛苦,很丑陋,而且毫无帮助。

void Foo::bar( int iApples )
{
int iBananas = iApples + g_fGrapes;
m_fMelons = g_fGrapes * iBananas;
s_dSpuds += m_fMelons;
}

匈牙利 是的告诉我们一些关于代码的新信息。我们现在了解到 Foo: : bar ()函数中有几个隐式转换。现在代码的问题是,匈牙利语前缀添加的信息的价值相对于视觉成本来说很小。C + + 类型系统包含许多特性,可以帮助类型很好地协同工作,或者提出编译器警告或错误。编译器帮助我们处理类型ーー我们不需要符号就可以做到这一点。我们可以很容易地推断出 Foo: : bar ()中的变量可能是数值型的,如果这就是我们所知道的全部,那么对于获得对函数的一般理解就足够了。因此,知道每个变量的精确类型的价值相对较低。然而,像“ s _ dSpuds”(甚至只是“ dSpuds”)这样的变量的丑陋之处是巨大的。因此,一个成本-收益分析拒绝匈牙利命名法,而 g _、 s _ 和 m _ 的好处在许多程序员眼中压倒了成本。

你永远不应该需要这样的前缀。如果这样的前缀给您提供了任何优势,那么您的编码风格通常需要修正,并且阻碍代码清晰的不是前缀。典型的坏变量名包括“ other”或“2”。你不能通过要求它是 mOther 来解决这个问题,你可以通过让开发人员思考这个变量在这个函数的上下文中做了什么来解决这个问题。也许他指的是 RemoteSide,或 newValue,或 Second TestListener,或该作用域中的某个东西。

这是一个有效的时代错误,仍然传播得太远。停止给你的变量加前缀,给它们正确的名字,它们的清晰度反映了它们被使用的时间。最多5行,你可以称之为“ i”没有混淆; 超过50行,你需要一个相当长的名称。

我喜欢变量名只给它们包含的值赋予一个意义,而不在名称中声明/实现它们的方式。我想知道这个值是什么意思,句号。也许我已经做了比平均数量更多的重构,但是我发现在名称中嵌入如何实现某些东西使得重构比它需要的更加单调乏味。指示声明对象成员的位置或方式的前缀是特定于实现的。

color = Red;

大多数时候,我不在乎 Red 是枚举还是结构,或者其他什么,如果函数太大,我不记得 color 是本地声明的还是成员,那么可能是时候将函数分解成更小的逻辑单元了。

如果你的循环复杂度如此之好,以至于你不能跟踪代码中正在发生的事情,除非在代码名称中嵌入特定于实现的线索,那么你很可能需要降低函数/方法的复杂性。

大多数情况下,我只在构造函数和初始化器中使用“ this”。

我使用 m _ for 成员变量只是为了利用智能感知和相关的 IDE 功能。在编写类的实现时,我可以输入 m _,然后看到所有 m _ 成员组合在一起的组合框。

当然,我可以毫无困难地生活下去,这就是我的工作方式。

根据联合打击战斗机 C + + 编码标准(2005年12月) :

AV 规则第67条

公共数据和受保护数据只应用于 基本原理: 一个类能够维护它的 通过控制对其数据的访问来保持不变。但是,类不能 控制对其成员的访问,如果这些成员是非私有的 类中的数据应该是私有的。

因此,“ m”前缀变得没有用处,因为所有数据都应该是私有的。

但是在指针前使用 p 前缀是一个好习惯,因为它是一个危险的变量。

这些约定中的许多都来自于没有经验丰富的编辑器的时代。我建议使用一个合适的 IDE,允许您为每种变量着色。到目前为止,颜色比任何前缀都容易辨认。

如果您需要获得关于变量的更多细节,任何现代 IDE 都应该能够通过将插入符号或光标移到变量上来向您显示它。如果以错误的方式使用变量(例如,使用。操作员)你会得到一个错误,无论如何。

就个人而言,我使用一个相对“ 很简单”系统来表示什么是变量

我将不同的“标志”组合在一起,然后是下划线,然后是内存类型,最后是名称。

我喜欢这样做,因为您可以尽可能快地缩小 IDE 完成过程中变量的数量。

我用的东西是:

m for member function
s for static
c for const/constexpr

然后是下划线 _

然后是变量内存类型

p for unowned pointer
v for list
r for reference
nothing for owned value

例如,如果我有一个成员变量,它是一个 int 列表

m_vName

对于一个静态常量指针,指向一个 int 列表的指针,我会把

sc_ppvName

这使我能够快速地了解变量的用途以及如何访问它。以及如何获取/删除值

前缀为 $sign,后缀为 _

class Demo {
public:
Demo() {
$name = "Harry Porter";
age_ = 18;
}
protected:
std::string $name;
int age_;
};