“ m_”变量前缀是什么意思?


154

我经常在教程,示例和其他主要与游戏开发相关的代码中看到m_用于变量m_World,,m_Sprites...)的前缀

人们为什么要m_在变量前添加前缀?



18
在盲目跟随匈牙利表示法之前,请对匈牙利表示法的真正含义进行一些历史检查。因为命名一个int iCounter只是毫无意义的。但是命名一个int xAnnotationPos和yAnnotationPos是合理的。使用语义版本。
2012年

有时,导入的模块为函数和变量添加前缀,这样您就不太可能用自己的代码覆盖它们。这是一种“保留”名称以用于特定用途的方法。
EarthmeLon 2012年

3
尽管经常恶毒地嘲笑“匈牙利”符号,但它表示变量范围的特殊风格确实具有一些真正的优势。除了标识变量的范围外,它还可以防止名称冲突,例如当本地变量,parm和成员都具有相同的意图,因此具有相同的“语义”名称时。这可以使大型代码库的维护更加简单,并且不易出错。
热门点击2012年

8
尽管有支持和反对任何编码标准的论点,但是这个问题显然询问了它的m_用途,但是这里有一半的回答是关于为什么每个人都认为他们目前最喜欢的是最好的评论。
cz

Answers:


108

这是用于定义作为成员变量的变量的典型编程实践。因此,以后使用它们时,无需查看定义它们的位置即可知道它们的范围。如果您已经知道范围,并且正在使用intelliSense之类的东西,那么这也很好,您可以从开始m_并显示所有成员变量的列表。匈牙利表示法的一部分,请参见此处示例中有关范围的部分。


51
对于命名约定而言,最糟糕的说法是,只需按ctrl + space键即可获取智能提示。
orlp 2012年

11
@nightcracker尽管我不喜欢该前缀,但他的意思是说,当您键入m_,然后键入“ CTRL + SPACE”(除非它是自动的),您会得到一个仅包含您的成员的列表。并不是一个很好的理由,但这是一个加号。
Sidar 2012年

13
值得一提的是,还有许多其他或多或少的标准方法可以完成相同的事情。“ m_variable”,“ m_Variable”,“ mVariable”,“ _ variable”,“ _ Variable” ...哪种方式“最佳”或“正确”(或是否完全做到)与“空格与制表符”。:)
特雷弗·鲍威尔

49
我更喜欢只使用“ this->”-有点使“ m_”成为多余的,甚至更好,因为它是由编译器强制执行的(理论上,您可以在任何变量类型上弹出“ m_”;不能使用“ this-> ”)。我的一部分希望C ++能够使“ this->”成为强制性标准。但这更多的是讨论而不是答案。

3
@LaurentCouvidou,您不能真正强制开发人员创建以m_任何一个为前缀的成员变量。
SomeWritesReserved 2012年

94

在“ 干净代码:敏捷软件技巧手册”中,明确建议不要使用此前缀:

您也不需要再为成员变量加上前缀m_。您的类和函数应该足够小,以至于不需要它们。

还有一个这样的示例(C#代码):

不良做法:

public class Part
{
    private String m_dsc; // The textual description

    void SetName(string name)
    {
        m_dsc = name;
    }
}

良好做法:

public class Part
{
    private String description;

    void SetDescription(string description)
    {
        this.description = description;
    }
}

我们用语言结构计数指成员变量中明确歧义(的情况下,即description成员和description参数)this


6
措辞可能是“有一个明确的建议
反对

我很高兴有人写了它
dmitreyg '18

另一个原因是在Java中,getter / setter假定为​​getName / setName,因此getM_name不好,您需要一个一个地处理它们。
里昂,

感谢您撰写本文。我只是想指出,您引用的书自2008年8月以来就已经淘汰了-我仍然在今天(2019年)的新代码中发现了这种不良做法。
alexlomba87

20

这是C ++中的常见做法。这是因为在C ++中,成员函数和成员变量不能具有相同的名称,并且getter函数的名称通常不带“ get”前缀。

class Person
{
   public:
      std::string name() const;

   private:
      std::string name; // This would lead to a compilation error.
      std::string m_name; // OK.
};

main.cpp:9:19: error: duplicate member 'name'
      std::string name;
                  ^
main.cpp:6:19: note: previous declaration is here
      std::string name() const;
                  ^
1 error generated.

http://coliru.stacked-crooked.com/a/f38e7dbb047687ad

“ m_”表示“成员”。前缀“ _”也很常见。

您不应该在通过使用不同的约定/语法来解决此问题的编程语言中使用它。


11

m_前缀通常用于成员变量-我认为它的主要优点是,它有助于在公共属性和支持它的私有成员变量之间建立清晰的区分:

int m_something

public int Something => this.m_something; 

对于后备变量,有一个一致的命名约定可能会有所帮助,而m_前缀是一种做到这一点的方法-一种不区分大小写的语言。

这有多有用,取决于您使用的语言和工具。具有强大的重构工具和智能感知功能的现代IDE对这样的约定的需求减少了,这当然不是唯一的方法,但是在任何情况下都值得一提。


5
如果您必须this.用自己的语言写,那m_真的没用。
罗斯兰

@Ruslan m_是将其与它所支持的属性区分开的-因此this.Something对于属性与this.m_something后备成员。这不是我更喜欢的约定,但是我最经常看到的是它在不区分大小写的语言(例如VB)中使用。
基思

1
为什么不,this.Something为了财产和this.something支持?还是this._something为了支持?this.m_something是多余的。我_something这样做是为了确保我输入Something
文字

@AustinWBryan看到我以前关于不区分大小写的语言的评论。是的,_仅靠前缀就可以完成任务,但这m_是惯例。这不是我个人使用的语言,但是如果您在代码中看到它,那是作者的意图。
基思

7

如其他答案所述,m_前缀用于指示变量是类成员。这与匈牙利表示法不同,因为它不指示变量的类型,而是指示其上下文。

m_在C ++中使用,但在某些其他必须使用“ this”或“ self”的语言中却没有使用。我不喜欢C ++使用'this->',因为它会使代码混乱。

另一个答案m_dsc是“不良做法”和“描述”;是“好的做法”,但这是一条鲱鱼,因为这里存在缩写的问题。

另一个答案是,键入会this弹出IntelliSense,但是任何不错的IDE都会有一个热键为当前班级成员弹出IntelliSense。


“但这是一条红鲱鱼”-好点。一个公平的比较是m_descriptionVS description
战斗

3

如许多其他响应所述,m_是表示成员变量的前缀。它曾经/曾经在C ++世界中使用,并且也传播到其他语言,包括Java。

在现代IDE中,它是完全多余的,因为语法高亮显示了哪些变量是局部变量,哪些是成员变量。但是,到90年代后期出现语法高亮时,该约定已经存在了很多年,并且已经确立下来(至少在C ++世界中如此)。

我不知道您指的是哪个教程,但是由于以下两个因素之一,我猜它们正在使用约定:

  • 它们是C ++教程,由习惯于m_约定的人编写,和/或...
  • 他们用纯文本(等距)编写代码,而没有突出显示语法,因此m_约定有助于使示例更清楚。

一个示例可能是这样:wiki.qt.io/How_to_Use_QSettings 由于Qt Creator正在使用突出显示,因此可能会出现第一个猜测。使用_object()用于类的私有对象和使用p_variable(如果是指针)的另一种约定可能是另一种约定,因为据我所知,这两者都不高,并且对我来说使用它似乎很有意义。
伊万诺维奇

3

洛克希德·马丁公司使用3前缀命名方案,这种方案非常好用,特别是在阅读其他人的代码时。

   Scope          Reference Type(*Case-by-Case)   Type

   member   m     pointer p                       integer n
   argument a     reference r                     short   n
   local    l                                     float   f
                                                  double  f
                                                  boolean b

所以...

int A::methodCall(float af_Argument1, int* apn_Arg2)
{
    lpn_Temp = apn_Arg2;
    mpf_Oops = lpn_Temp;  // Here I can see I made a mistake, I should not assign an int* to a float*
}

考虑它的价值。


太棒了 感谢您的“示例”。当您编辑200,000行代码时,真正有用的地方。
jiveturkey

1
无需防御。老实说,我正在尝试通过向您显示答案有误来帮助您。然后,让我更加清楚:如果有5行或20万行代码,则无所谓:编译器将不允许您使用不兼容的指针类型进行赋值。因此,评论中提出的观点尚无定论。
卡西奥雷南

并不是说要防守。抱歉。
jiveturkey

这是匈牙利表示法的一种变体,在现代语言中没有意义……
doc

2

为了完成当前的答案,并且由于问题不是特定于语言的,因此某些C项目使用前缀m_来定义特定于文件g_的全局变量-以及作用域大于定义的文件的全局变量。
在这种情况下,用前缀定义的全局变量m_ 应定义为static

有关使用此约定的项目示例,请参见EDK2(UEFI开源实现)编码约定。


1

我还没有看到的一个论点是,m_可以使用诸如这样的前缀来防止名称与#define'd宏冲突。

正则表达式搜索#define [a-z][A-Za-z0-9_]*[^(]/usr/include/term.h从诅咒/ ncurses的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.