为什么在 C++ 中的数据成员上使用像 m_ 这样的前缀 课程?

发布于 2024-07-29 21:51:27 字数 461 浏览 7 评论 0原文

许多 C++ 代码使用语法约定来标记数据成员。 常见的示例包括

  • 用于公共成员的 m_memberName (其中完全使用公共成员)
  • _memberName 用于私有成员或所有成员

其他人尝试使用 this->member 强制执行 每当使用数据成员时。

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

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

这个约定现在在 C++ 中仍然有用吗?或者它只是一个不合时宜的东西,特别是因为它在各个库中的使用如此不一致? 其他语言难道没有表明可以不用成员前缀吗?

A lot of C++ code uses syntactical conventions for marking up data members. Common examples include

  • m_memberName for public members (where public members are used at all)
  • _memberName for private members or all members

Others try to enforce using this->member whenever a data member is used.

In my experience, most larger code bases fail at applying such rules consistently.

In other languages, these conventions are far less widespread. I see it only occasionally in Java or C# code. I think I have never seen it in Ruby or Python code. Thus, there seems to be a trend with more modern languages to not use special markup for data members.

Is this convention still useful today in C++ or is it just an anachronism, especially as it is used so inconsistently across libraries? Haven't the other languages shown that one can do without member prefixes?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

预谋 2024-08-05 21:51:59

其中许多惯例都是在没有成熟编辑的时代产生的。 我建议使用合适的 IDE,它允许您为每种变量着色。 颜色比任何前缀都更容易识别。

如果您需要获得有关变量的更多详细信息,任何现代 IDE 都应该能够通过将插入符号或光标移到变量上来向您显示。 如果您以错误的方式使用变量(例如带有 . 运算符的指针),无论如何您都会收到错误。

Many of those conventions are from a time without sophisticated editors. I would recommend using a proper IDE that allows you to color every kind of variable. Color is by far easier to spot than any prefix.

If you need to get even more detail on a variable any modern IDE should be able to show it to you by moving the caret or cursor over it. And if you use a variable in a wrong way (for instance a pointer with the . operator) you will get an error, anyway.

萌无敌 2024-08-05 21:51:58

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

AV 规则 67

公共和受保护的数据只能用于
结构——而不是类。 理由:一个类能够维持它的
通过控制对其数据的访问来保持不变。 然而,一个类不能
如果这些成员是非私有的,则控制对其成员的访问。 因此所有
类中的数据应该是私有的。

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

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

According to JOINT STRIKE FIGHTER AIR VEHICLE C++ CODING STANDARDS (december 2005):

AV Rule 67

Public and protected data should only be used in
structs—not classes. Rationale: A class is able to maintain its
invariant by controlling access to its data. However, a class cannot
control access to its members if those members non-private. Hence all
data in a class should be private.

Thus, the "m" prefix becomes unuseful as all data should be private.

But it is a good habit to use the p prefix before a pointer as it is a dangerous variable.

岁月如刀 2024-08-05 21:51:58

用结构体包装所有私有数据

class Demo {
public:
    Demo() {
        self.name = "Harry Porter";
        self.age = 18;
    }
protected:
    struct self_type {
        std::string name;
        int age;
    } self = {};
};

Wrap all private data with a struct

class Demo {
public:
    Demo() {
        self.name = "Harry Porter";
        self.age = 18;
    }
protected:
    struct self_type {
        std::string name;
        int age;
    } self = {};
};
拥醉 2024-08-05 21:51:57

您永远不需要这样的前缀。 如果这样的前缀为您提供了任何优势,那么您的编码风格通常需要修复,并且并不是前缀使您的代码变得不清晰。 典型的错误变量名称包括“other”或“2”。 您不需要通过要求它是 mOther 来解决这个问题,您可以通过让开发人员考虑该变量在该函数的上下文中正在做什么来解决它。 也许他指的是remoteSide、newValue、secondTestListener 或该范围内的其他内容。

这是一种有效的不合时宜的做法,但仍然传播得太远。 停止为变量添加前缀,并为它们提供适当的名称,其清晰度反映了它们的使用时间。 最多 5 行,您可以将其称为“i”而不会混淆; 超过 50 行,你需要一个很长的名字。

You should never need such a prefix. If such a prefix offers you any advantage, your coding style in general needs fixing, and it's not the prefix that's keeping your code from being clear. Typical bad variable names include "other" or "2". You do not fix that with requiring it to be mOther, you fix it by getting the developer to think about what that variable is doing there in the context of that function. Perhaps he meant remoteSide, or newValue, or secondTestListener or something in that scope.

It's an effective anachronism that's still propagated too far. Stop prefixing your variables and give them proper names whose clarity reflects how long they're used. Up to 5 lines you could call it "i" without confusion; beyond 50 lines you need a pretty long name.

陪你搞怪i 2024-08-05 21:51:56

我几乎从不在变量名前面使用前缀。 如果您使用足够好的 IDE,您应该能够轻松重构和查找参考。 我使用非常清晰的名称,并且不担心变量名称很长。 对于这种哲学,我也从未遇到过范围问题。

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

I almost never use prefixes in front of my variable names. If you're using a decent enough IDE you should be able to refactor and find references easily. I use very clear names and am not afraid of having long variable names. I've never had trouble with scope either with this philosophy.

The only time I use a prefix would be on the signature line. I'll prefix parameters to a method with _ so I can program defensively around them.

扛刀软妹 2024-08-05 21:51:55

Code Complete 建议使用 m_varname 作为成员变量。

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

Code Complete recommends m_varname for member variables.

While I've never thought the m_ notation useful, I would give McConnell's opinion weight in building a standard.

划一舟意中人 2024-08-05 21:51:54

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

It is useful to differentiate between member variables and local variables due to memory management. Broadly speaking, heap-allocated member variables should be destroyed in the destructor, while heap-allocated local variables should be destroyed within that scope. Applying a naming convention to member variables facilitates correct memory management.

洛阳烟雨空心柳 2024-08-05 21:51:53

我使用 m_ 作为成员变量只是为了利用 Intellisense 和相关 IDE 功能。 当我编写类的实现时,我可以输入 m_ 并查看包含所有 m_ 成员组合在一起的组合框。

但当然,没有 m_ ,我也可以生活。 这只是我的工作风格。

I use m_ for member variables just to take advantage of Intellisense and related IDE-functionality. When I'm coding the implementation of a class I can type m_ and see the combobox with all m_ members grouped together.

But I could live without m_ 's without problem, of course. It's just my style of work.

始终不够 2024-08-05 21:51:52

我喜欢变量名只赋予它们所包含的值一个含义,而将它们的声明/实现方式保留在名称之外。 我想知道这个值意味着什么。 也许我已经完成了超过平均数量的重构,但我发现在名称中嵌入某些内容的实现方式会使重构变得比实际需要的更加乏味。 指示对象成员声明位置或方式的前缀是特定于实现的。

color = Red;

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

如果您的圈复杂度如此之大,以至于您无法跟踪代码中发生的情况,而无需在事物名称中嵌入特定于实现的线索,那么您很可能需要降低函数/方法的复杂性。

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

I like variable names to give only a meaning to the values they contain, and leave how they are declared/implemented out of the name. I want to know what the value means, period. Maybe I've done more than an average amount of refactoring, but I find that embedding how something is implemented in the name makes refactoring more tedious than it needs to be. Prefixes indicating where or how object members are declared are implementation specific.

color = Red;

Most of the time, I don't care if Red is an enum, a struct, or whatever, and if the function is so large that I can't remember if color was declared locally or is a member, it's probably time to break the function into smaller logical units.

If your cyclomatic complexity is so great that you can't keep track of what is going on in the code without implementation-specific clues embedded in the names of things, most likely you need to reduce the complexity of your function/method.

Mostly, I only use 'this' in constructors and initializers.

肥爪爪 2024-08-05 21:51:52

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

In python leading double underscores are used to emulate private members. For more details see this answer

谢绝鈎搭 2024-08-05 21:51:50

我认为,如果需要前缀来区分类成员、成员函数参数和局部变量,要么是函数太大,要么是变量命名不当。 如果它不适合屏幕,以便您可以轻松地看到是什么,请重构。

鉴于它们通常是在远离它们使用的地方声明的,我发现全局常量(和全局变量,尽管在我看来很少需要使用它们)的命名约定是有意义的。 但除此之外,我认为没有太大必要。

也就是说,我曾经在所有私有类成员的末尾加下划线。 由于我的所有数据都是私有的,这意味着成员有一个尾随下划线。 我通常不再在新的代码库中这样做,但由于作为一名程序员,您主要使用旧代码,因此我仍然经常这样做。 我不确定我对这个习惯的容忍是否来自于我以前总是这样做并且现在仍然定期这样做,或者它是否真的比成员变量的标记更有意义。

I think that, if you need prefixes to distinguish class members from member function parameters and local variables, either the function is too big or the variables are badly named. If it doesn't fit on the screen so you can easily see what is what, refactor.

Given that they often are declared far from where they are used, I find that naming conventions for global constants (and global variables, although IMO there's rarely ever a need to use those) make sense. But otherwise, I don't see much need.

That said, I used to put an underscore at the end of all private class members. Since all my data is private, this implies members have a trailing underscore. I usually don't do this anymore in new code bases, but since, as a programmer, you mostly work with old code, I still do this a lot. I'm not sure whether my tolerance for this habit comes from the fact that I used to do this always and am still doing it regularly or whether it really makes more sense than the marking of member variables.

十雾 2024-08-05 21:51:49

我使用它是因为 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;
}

I use it because VC++'s Intellisense can't tell when to show private members when accessing out of the class. The only indication is a little "lock" symbol on the field icon in the Intellisense list. It just makes it easier to identify private members(fields) easier. Also a habit from C# to be honest.

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;
}
っ〆星空下的拥抱 2024-08-05 21:51:48

我们的项目一直使用“its”作为成员数据的前缀,使用“the”作为参数的前缀,而没有本地变量的前缀。 这有点可爱,但它被我们系统的早期开发人员采用,因为他们看到我们当时使用的一些商业源库(XVT 或 RogueWave - 也许两者都)将其用作约定。 所以你会得到这样的结果:

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

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

只需使用这个-> 还不够好 - 我们对减少歧义并不像对减少编码错误那么感兴趣,并且用本地范围的标识符屏蔽名称可能会很痛苦。 诚然,某些编译器可能可以选择在您在更大范围内屏蔽名称的情况下发出警告,但如果您正在使用大量第三方库,而这些库碰巧选择了这些库,那么这些警告可能会变得很麻烦。未使用的变量的名称偶尔会与您自己的变量发生冲突。

至于它/它本身 - 老实说,我发现它比下划线更容易打字(作为盲打打字员,我尽可能避免使用下划线 - 太多超出主行),而且我发现它比神秘的下划线更具可读性。

Our project has always used "its" as a prefix for member data, and "the" as a prefix for parameters, with no prefix for locals. It's a little cutesy, but it was adopted by the early developers of our system because they saw it used as a convention by some commercial source libraries we were using at the time (either XVT or RogueWave - maybe both). So you'd get something like this:

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

The big reason I see for scoping prefixes (and no others - I hate Hungarian notation) is that it prevents you from getting into trouble by writing code where you think you're referring to one variable, but you're really referring to another variable with the same name defined in the local scope. It also avoids the problem of coming up with a variable names to represent that same concept, but with different scopes, like the example above. In that case, you would have to come up with some prefix or different name for the parameter "theName" anyway - why not make a consistent rule that applies everywhere.

Just using this-> isn't really good enough - we're not as interested in reducing ambiguity as we are in reducing coding errors, and masking names with locally scoped identifiers can be a pain. Granted, some compilers may have the option to raise warnings for cases where you've masked the name in a larger scope, but those warnings may become a nuisance if you're working with a large set of third party libraries that happen to have chosen names for unused variables that occasionally collide with your own.

As for the its/the itself - I honestly find it easier to type than underscores (as a touch typist, I avoid underscores whenever possible - too much stretching off the home rows), and I find it more readable than a mysterious underscore.

羁绊已千年 2024-08-05 21:51:46

C++ 成员变量前缀的最初想法是存储编译器不知道的附加类型信息。 例如,您可以有一个固定长度字符的字符串,另一个是可变的并以“\0”结尾的字符串。 对于编译器来说,它们都是 char *,但如果您尝试从一个复制到另一个,则会遇到巨大的麻烦。 所以,我突然想到,


char *aszFred = "嗨,我是一个以 null 结尾的字符串";
char *arrWilma = {'O', 'o', 'p', 's'};

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

然后神奇的事情就发生了。编译器会对这个语句非常满意:


strcpy(arrWilma, aszFred);

但是,作为一个人,您可以看着它并说“嘿,这些变量实际上并不是同一类型,我不能这样做”。

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

最后,我应该提到两点:

  • 明智地使用 C++ 功能可以让编译器知道必须在原始 C 风格变量中编码的信息。 您可以创建只允许有效操作的类。 应尽可能地做到这一点。
  • 如果您的代码块太长,以至于您在使用变量之前忘记了变量的类型,那么它们就太长了。 不要使用名称,重新组织。

The original idea for prefixes on C++ member variables was to store additional type information that the compiler didn't know about. So for example, you could have a string that's a fixed length of chars, and another that's variable and terminated by a '\0'. To the compiler they're both char *, but if you try to copy from one to the other you get in huge trouble. So, off the top of my head,


char *aszFred = "Hi I'm a null-terminated string";
char *arrWilma = {'O', 'o', 'p', 's'};

where "asz" means this variable is "ascii string (zero-terminated) and "arr" means this variable is a character array.

Then the magic happens. The compiler will be perfectly happy with this statement:


strcpy(arrWilma, aszFred);

But you, as a human, can look at it and say "hey, those variables aren't really the same type, I can't do that".

Unfortunately a lot places use standards such as "m_" for member variables, "i" for integers no matter how used, "cp" for char pointers. In other words they're duplicating what the compiler knows, and making the code hard to read at the same time. I believe this pernicious practice should be outlawed by statute and subject to harsh penalties.

Finally, there's two points I should mention:

  • Judicious use of C++ features allows the compiler to know the information you had to encode in raw C-style variables. You can make classes that will only allow valid operations. This should be done as much as practical.
  • If your code blocks are so long that you forget what type a variable is before you use it, they are way too long. Don't use names, re-organize.
尘世孤行 2024-08-05 21:51:45

正如其他人已经说过的,重要的是要口语化(根据您正在编写的代码库调整命名样式和约定)并保持一致。

多年来,我一直致力于使用“this->”的大型代码库。 约定以及对成员变量使用后缀下划线表示法。 多年来,我也参与过较小的项目,其中一些项目没有任何命名成员变量的约定,而其他项目则有不同的成员变量命名约定。 在这些较小的项目中,我一直发现那些缺乏任何约定的项目是最难快速进入和理解的。

我对命名非常严格。 我会为类或变量的名称而苦恼,如果我不能想出一些我认为“好”的东西,我会选择将其命名为一些无意义的名称,并提供一个评论来描述它的真正含义是。 这样,至少这个名字的含义正是我想要表达的意思——不多也不少。 通常,在使用一段时间后,我会发现这个名称实际上应该是什么,并且可以返回并进行适当的修改或重构。

关于 IDE 完成工作这一主题的最后一点——这一切都很好,但在我执行最紧急工作的环境中,IDE 通常不可用。 有时,此时唯一可用的就是“vi”的副本。 另外,我见过许多 IDE 代码补全传播愚蠢行为的情况,例如名称拼写错误。 因此,我宁愿不必依赖 IDE 拐杖。

As others have already said, the importance is to be colloquial (adapt naming styles and conventions to the code base in which you're writing) and to be consistent.

For years I have worked on a large code base that uses both the "this->" convention as well as using a postfix underscore notation for member variables. Throughout the years I've also worked on smaller projects, some of which did not have any sort of convention for naming member variables, and other which had differing conventions for naming member variables. Of those smaller projects, I've consistently found those which lacked any convention to be the most difficult to jump into quickly and understand.

I'm very anal-retentive about naming. I will agonize over the name to be ascribed to a class or variable to the point that, if I cannot come up with something that I feel is "good", I will choose to name it something nonsensical and provide a comment describing what it really is. That way, at least the name means exactly what I intend it to mean--nothing more and nothing less. And often, after using it for a little while, I discover what the name should really be and can go back and modify or refactor appropriately.

One last point on the topic of an IDE doing the work--that's all nice and good, but IDEs are often not available in environments where I have perform the most urgent work. Sometimes the only thing available at that point is a copy of 'vi'. Also, I've seen many cases where IDE code completion has propagated stupidity such as incorrect spelling in names. Thus, I prefer to not have to rely on an IDE crutch.

狼亦尘 2024-08-05 21:51:43

IMO,这是个人的。 我根本没有添加任何前缀。 无论如何,如果代码是公开的,我认为它应该最好有一些前缀,这样它可以更具可读性。

大公司通常使用自己的所谓“开发人员规则”。
顺便说一句,我看到的最有趣但最聪明的是 DRY KISS(不要重复自己。保持简单,愚蠢)。 :-)

IMO, this is personal. I'm not putting any prefixes at all. Anyway, if code is meaned to be public, I think it should better has some prefixes, so it can be more readable.

Often large companies are using it's own so called 'developer rules'.
Btw, the funniest yet smartest i saw was DRY KISS (Dont Repeat Yourself. Keep It Simple, Stupid). :-)

雪化雨蝶 2024-08-05 21:51:41

当您有一个大的方法或代码块时,可以很方便地立即知道您是否使用局部变量或成员。 这是为了避免错误并提高清晰度!

When you have a big method or code blocks, it's convenient to know immediately if you use a local variable or a member. it's to avoid errors and for better clearness !

执手闯天涯 2024-08-05 21:51:40

其他语言将使用编码约定,只是它们往往有所不同。 例如,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 前缀,因为您的用法以此为前缀。 在编译器强制方法中表示同样的事情。

这并不一定意味着其他方法不好,人们会坚持有效的方法。

Other languages will use coding conventions, they just tend to be different. C# for example has probably two different styles that people tend to use, either one of the C++ methods (_variable, mVariable or other prefix such as Hungarian notation), or what I refer to as the StyleCop method.

private int privateMember;
public int PublicMember;

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

In the end, it becomes what people know, and what looks best. I personally think code is more readable without Hungarian notation, but it can become easier to find a variable with intellisense for example if the Hungarian notation is attached.

In my example above, you don't need an m prefix for member variables because prefixing your usage with this. indicates the same thing in a compiler-enforced method.

This doesn't necessarily mean the other methods are bad, people stick to what works.

○闲身 2024-08-05 21:51:39

其他人尝试强制使用
this->member 每当成为成员时
使用变量

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

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

Others try to enforce using
this->member whenever a member
variable is used

That is usually because there is no prefix. The compiler needs enough information to resolve the variable in question, be it a unique name because of the prefix, or via the this keyword.

So, yes, I think prefixes are still useful. I, for one, would prefer to type '_' to access a member rather than 'this->'.

巡山小妖精 2024-08-05 21:51:38

这些约定就是这样。 大多数商店使用代码约定来简化代码可读性,以便任何人都可以轻松查看一段代码并快速破译公共成员和私有成员等内容。

Those conventions are just that. Most shops use code conventions to ease code readability so anyone can easily look at a piece of code and quickly decipher between things such as public and private members.

赠意 2024-08-05 21:51:37

一些回应侧​​重于重构,而不是命名约定,作为提高可读性的方法。 我不认为其中一个可以替代另一个。

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

我希望大家能够清楚地了解这与成员命名约定的关系:当成员有统一的前缀时,我根本不需要回头看; 我知道在源文件中甚至找不到该声明。

我确信我一开始并不喜欢这些风格。 然而,随着时间的推移,在持续使用它们的环境中工作,我优化了我的思维以利用它们。 我认为,鉴于持续的使用,许多目前对它们感到不舒服的人可能也会更喜欢它们。

Some responses focus on refactoring, rather than naming conventions, as the way to improve readability. I don't feel that one can replace the other.

I've known programmers who are uncomfortable with using local declarations; they prefer to place all the declarations at the top of a block (as in C), so they know where to find them. I've found that, where scoping allows for it, declaring variables where they're first used decreases the time that I spend glancing backwards to find the declarations. (This is true for me even for small functions.) That makes it easier for me to understand the code I'm looking at.

I hope it's clear enough how this relates to member naming conventions: When members are uniformly prefixed, I never have to look back at all; I know the declaration won't even be found in the source file.

I'm sure that I didn't start out preferring these styles. Yet over time, working in environments where they were used consistently, I optimized my thinking to take advantage of them. I think it's possible that many folks who currently feel uncomfortable with them would also come to prefer them, given consistent usage.

白云不回头 2024-08-05 21:51:36

我不认为一种语法比另一种语法具有真正的价值。 正如您所提到的,这一切都归结为源文件之间的一致性。

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

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

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

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

I don't think one syntax has real value over another. It all boils down, like you mentionned, to uniformity across the source files.

The only point where I find such rules interesting is when I need 2 things named identicaly, for example :

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

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

I use it to differentiate the two. Also when I wrap calls, like from windows Dll, RecvPacket(...) from the Dll might be wrapped in RecvPacket(...) in my code. In these particular occasions using a prefix like "_" might make the two look alike, easy to identify which is which, but different for the compiler

苍暮颜 2024-08-05 21:51:35

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

请考虑:

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() 或以某种方式修饰成员变量。

The main reason for a member prefix is to distinguish between a member function and a member variable with the same name. This is useful if you use getters with the name of the thing.

Consider:

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_;
};

The member variable could not be named full_name in this case. You need to rename the member function to get_full_name() or decorate the member variable somehow.

明媚如初 2024-08-05 21:51:34

在阅读成员函数时,了解谁“拥有”每个变量对于理解变量的含义绝对重要。 在这样的函数中:

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;
}

一开始这些可能需要一些时间来适应——但是,编程中有什么不需要适应的呢? 有一天,连 { 和 } 对你来说都显得很奇怪。 一旦您习惯了它们,它们就会帮助您更快地理解代码。

(使用“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_的好处压倒了成本。

When reading through a member function, knowing who "owns" each variable is absolutely essential to understanding the meaning of the variable. In a function like this:

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

...it's easy enough to see where apples and bananas are coming from, but what about grapes, melons, and spuds? Should we look in the global namespace? In the class declaration? Is the variable a member of this object or a member of this object's class? Without knowing the answer to these questions, you can't understand the code. And in a longer function, even the declarations of local variables like apples and bananas can get lost in the shuffle.

Prepending a consistent label for globals, member variables, and static member variables (perhaps g_, m_, and s_ respectively) instantly clarifies the situation.

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

These may take some getting used to at first—but then, what in programming doesn't? There was a day when even { and } looked weird to you. And once you get used to them, they help you understand the code much more quickly.

(Using "this->" in place of m_ makes sense, but is even more long-winded and visually disruptive. I don't see it as a good alternative for marking up all uses of member variables.)

A possible objection to the above argument would be to extend the argument to types. It might also be true that knowing the type of a variable "is absolutely essential to understanding the meaning of the variable." If that is so, why not add a prefix to each variable name that identifies its type? With that logic, you end up with Hungarian notation. But many people find Hungarian notation laborious, ugly, and unhelpful.

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

Hungarian does tell us something new about the code. We now understand that there are several implicit casts in the Foo::bar() function. The problem with the code now is that the value of the information added by Hungarian prefixes is small relative to the visual cost. The C++ type system includes many features to help types either work well together or to raise a compiler warning or error. The compiler helps us deal with types—we don't need notation to do so. We can infer easily enough that the variables in Foo::bar() are probably numeric, and if that's all we know, that's good enough for gaining a general understanding of the function. Therefore the value of knowing the precise type of each variable is relatively low. Yet the ugliness of a variable like "s_dSpuds" (or even just "dSpuds") is great. So, a cost-benefit analysis rejects Hungarian notation, whereas the benefit of g_, s_, and m_ overwhelms the cost in the eyes of many programmers.

有木有妳兜一样 2024-08-05 21:51:34

我不能说它有多广泛,但就我个人而言,我总是(并且一直)在我的成员变量前加上“m”前缀。 例如:

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

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

I can't say how widespred it is, but speaking personally, I always (and have always) prefixed my member variables with 'm'. E.g.:

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

It's the only form of prefixing I do use (I'm very anti Hungarian notation) but it has stood me in good stead over the years. As an aside, I generally detest the use of underscores in names (or anywhere else for that matter), but do make an exception for preprocessor macro names, as they are usually all uppercase.

时光病人 2024-08-05 21:51:33

最近我一直倾向于选择 m_ 前缀而不是根本没有前缀,原因并不在于标记成员变量很重要,而是它避免了歧义,假设你有这样的代码:

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需要>自我。。

Lately I have been tending to prefer m_ prefix instead of having no prefix at all, the reasons isn't so much that its important to flag member variables, but that it avoids ambiguity, say you have code like:

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

That of cause doesn't work, only one foo allowed. So your options are:

  • this->foo = foo;

    I don't like it, as it causes parameter shadowing, you no longer can use g++ -Wshadow warnings, its also longer to type then m_. You also still run into naming conflicts between variables and functions when you have a int foo; and a int foo();.

  • foo = foo_; or foo = arg_foo;

    Been using that for a while, but it makes the argument lists ugly, documentation shouldn't have do deal with name disambiguity in the implementation. Naming conflicts between variables and functions also exist here.

  • m_foo = foo;

    API Documentation stays clean, you don't get ambiguity between member functions and variables and its shorter to type then this->. Only disadvantage is that it makes POD structures ugly, but as POD structures don't suffer from the name ambiguity in the first place, one doesn't need to use it with them. Having a unique prefix also makes a few search&replace operations easier.

  • foo_ = foo;

    Most of the advantages of m_ apply, but I reject it for aesthetic reasons, a trailing or leading underscore just makes the variable look incomplete and unbalanced. m_ just looks better. Using m_ is also more extendable, as you can use g_ for globals and s_ for statics.

PS: The reason why you don't see m_ in Python or Ruby is because both languages enforce the their own prefix, Ruby uses @ for member variables and Python requires self..

放手` 2024-08-05 21:51:32

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

class Foo
{
   private:
      int bar_;

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

I prefer postfix underscores, like such:

class Foo
{
   private:
      int bar_;

   public:
      int bar() { return bar_; }
};
别靠近我心 2024-08-05 21:51:31

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

_Foo

_L

都是保留字,而

_foo

_l

则不是。 还有其他情况不允许在小写字母之前使用下划线。 在我的具体案例中,我发现 _L 恰好被 Visual C++ 2005 保留,并且冲突产生了一些意想不到的结果。

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

以下是有关保留哪些标识符的链接:
在字符串中使用下划线的规则是什么C++ 标识符?

You have to be careful with using a leading underscore. A leading underscore before a capital letter in a word is reserved.
For example:

_Foo

_L

are all reserved words while

_foo

_l

are not. There are other situations where leading underscores before lowercase letters are not allowed. In my specific case, I found the _L happened to be reserved by Visual C++ 2005 and the clash created some unexpected results.

I am on the fence about how useful it is to mark up local variables.

Here is a link about which identifiers are reserved:
What are the rules about using an underscore in a C++ identifier?

2024-08-05 21:51:30

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

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

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

To 。对我来说,这是两全其美的,我有一个可以在需要时使用的前缀,并且只要有可能我就可以将其省略。

当然,明显的反驳是“是的,但是你可以”。一眼就能看出变量是否是类成员”。

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

在实践中,我发现这非常有效。作为一个额外的好处,它允许我轻松地将局部变量提升为类成员(或相反),而无需 。

最重要的是,它是一致的!我不需要做任何特殊的事情或记住任何约定来保持一致性。


顺便说一句,您不应该使用前导下划线 。

该标准保留了所有以双下划线或下划线后跟大写字母开头的名称。它还保留了全局命名空间中以单下划线开头的所有名称 /em>

因此,带有前导下划线后跟小写字母的类成员是合法的,但迟早您会对以大写字母开头的标识符执行相同的操作,否则会破坏上述其中之一因此,

如果您想在变量名称中对范围进行编码,则更容易避免使用后缀下划线或 m_ 或仅 m 前缀。

I generally don't use a prefix for member variables.

I used to use a m prefix, until someone pointed out that "C++ already has a standard prefix for member access: this->.

So that's what I use now. That is, when there is ambiguity, I add the this-> prefix, but usually, no ambiguity exists, and I can just refer directly to the variable name.

To me, that's the best of both worlds. I have a prefix I can use when I need it, and I'm free to leave it out whenever possible.

Of course, the obvious counter to this is "yes, but then you can't see at a glance whether a variable is a class member or not".

To which I say "so what? If you need to know that, your class probably has too much state. Or the function is too big and complicated".

In practice, I've found that this works extremely well. As an added bonus it allows me to promote a local variable to a class member (or the other way around) easily, without having to rename it.

And best of all, it is consistent! I don't have to do anything special or remember any conventions to maintain consistency.


By the way, you shouldn't use leading underscores for your class members. You get uncomfortably close to names that are reserved by the implementation.

The standard reserves all names starting with double underscore or underscore followed by capital letter. It also reserves all names starting with a single underscore in the global namespace.

So a class member with a leading underscore followed by a lower-case letter is legal, but sooner or late you're going to do the same to an identifier starting with upper-case, or otherwise break one of the above rules.

So it's easier to just avoid leading underscores. Use a postfix underscore, or a m_ or just m prefix if you want to encode scope in the variable name.

失而复得 2024-08-05 21:51:30

我完全赞成前缀做得好

我认为(系统)匈牙利表示法是造成前缀的大部分“坏名声”的原因。

这种表示法在强类型语言中基本上没有意义,例如在 C++ 中“lpsz”告诉你你的字符串是一个指向 nul 终止字符串的长指针,当:分段架构是古老的历史,C++ 字符串按照常见约定是指向 nul 终止字符串的指针char 数组,知道“customerName”是一个字符串并不是那么困难!

但是,我确实使用前缀来指定变量的用法(本质上是“Apps Hungarian”,尽管我更喜欢避免使用“匈牙利语”一词,因为它与“系统匈牙利语”有不好且不公平的关联),并且这是一种非常方便的节省时间减少错误的方法。

我使用:

  • m 代表成员
  • c 代表常量/只读
  • p 代表指针(pp 代表指向指针的指针)
  • v 代表易失性
  • s 代表静态
  • i 代表索引和迭代器
  • e 代表事件

我希望创建类型的地方清楚,我使用标准后缀(例如列表、组合框等)。

这使得程序员无论何时看到/使用变量时都知道该变量的用法。 可以说,最重要的情况是指针的“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;
    }
}

您可以在这里看到:

  • 成员和参数之间没有混淆
  • 索引/迭代器和之间没有混淆items
  • 使用一组明确相关的变量(项目列表、指针和索引),避免了通用(模糊)名称(如“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”是否是本地、参数、成员或常量。 我不必将手移到鼠标上,这样我就可以将指针悬停在“数据”上,然后等待工具提示(有时永远不会出现)弹出。 因此,程序员可以更快地阅读和理解代码,因为他们不会浪费时间上下搜索或等待。

(如果您认为自己不浪费时间上下搜索来解决问题,请找到一些您一年前编写但尚未查看的代码
自从。 打开文件并跳到一半而不阅读它。
在你不知道是否可以之前,看看你能从这一点读到多远
某物是成员、参数或本地。 现在跳转到另一个随机
位置...这就是我们单身时整天所做的事情
单步执行别人的代码或尝试了解如何
调用他们的函数)

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

“this”表示法旨在解决歧义 - 但为什么有人会故意编写可能歧义的代码呢? 歧义迟早会导致错误。 在某些语言中,“this”不能用于静态成员,因此您必须在编码风格中引入“特殊情况”。 我更喜欢有一个简单的、适用于任何地方的编码规则——明确、明确和一致。

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

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


反对的论点

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

  • “前缀方案是坏/邪恶的”。 我同意“m_lpsz”及其类似内容经过深思熟虑且完全无用。 这就是为什么我建议使用精心设计的符号来支持您的需求,而不是复制不适合您的上下文的内容。 (使用正确的工具来完成工作)。

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

  • “前缀让我很困惑”。 就像每个工具一样,直到您学会如何使用它为止。 一旦你的大脑习惯了命名模式,它就会自动过滤掉信息,你就不会介意前缀的存在了。 但你必须扎实地使用这样的方案一两周,然后你才能真正变得“流利”。 就在那时,很多人查看旧代码并开始想知道如果没有良好的前缀方案,他们是如何管理的。

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

  • 只需等待变量上弹出工具提示即可找到(部分)信息。 是的。 在支持的情况下,对于某些类型的前缀,当您的代码干净地编译后,等待一段时间后,您可以通读描述并立即找到该前缀将传达的信息。 我觉得前缀是一种更简单、更可靠、更高效的方法。

  • “更多的是打字”。 真的吗? 多一个完整的角色? 或者是 - 使用 IDE 自动完成工具,通常会减少输入,因为每个前缀字符都会显着缩小搜索空间。 按“e”,您班级中的三个事件会在智能感知中弹出。 按“c”,会列出五个常量。

  • “我可以使用 this-> 代替 m。 嗯,是的,你可以。 但这只是一个更丑陋、更冗长的前缀! 只是它带来了更大的风险(尤其是在团队中),因为对于编译器来说它是可选的,因此它的使用经常不一致。 另一方面,m 是简短、清晰、明确且非可选的,因此使用它更难犯错误。

I'm all in favour of prefixes done well.

I think (System) Hungarian notation is responsible for most of the "bad rap" that prefixes get.

This notation is largely pointless in strongly typed languages e.g. in C++ "lpsz" to tell you that your string is a long pointer to a nul terminated string, when: segmented architecture is ancient history, C++ strings are by common convention pointers to nul-terminated char arrays, and it's not really all that difficult to know that "customerName" is a string!

However, I do use prefixes to specify the usage of a variable (essentially "Apps Hungarian", although I prefer to avoid the term Hungarian due to it having a bad and unfair association with System Hungarian), and this is a very handy timesaving and bug-reducing approach.

I use:

  • m for members
  • c for constants/readonlys
  • p for pointer (and pp for pointer to pointer)
  • v for volatile
  • s for static
  • i for indexes and iterators
  • e for events

Where I wish to make the type clear, I use standard suffixes (e.g. List, ComboBox, etc).

This makes the programmer aware of the usage of the variable whenever they see/use it. Arguably the most important case is "p" for pointer (because the usage changes from var. to var-> and you have to be much more careful with pointers - NULLs, pointer arithmetic, etc), but all the others are very handy.

For example, you can use the same variable name in multiple ways in a single function: (here a C++ example, but it applies equally to many languages)

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

You can see here:

  • No confusion between member and parameter
  • No confusion between index/iterator and items
  • Use of a set of clearly related variables (item list, pointer, and index) that avoid the many pitfalls of generic (vague) names like "count", "index".
  • Prefixes reduce typing (shorter, and work better with auto-completion) than alternatives like "itemIndex" and "itemPtr"

Another great point of "iName" iterators is that I never index an array with the wrong index, and if I copy a loop inside another loop I don't have to refactor one of the loop index variables.

Compare this unrealistically simple example:

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

(which is hard to read and often leads to use of "i" where "j" was intended)

with:

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

(which is much more readable, and removes all confusion over indexing. With auto-complete in modern IDEs, this is also quick and easy to type)

The next benefit is that code snippets don't require any context to be understood. I can copy two lines of code into an email or a document, and anyone reading that snippet can tell the difference between all the members, constants, pointers, indexes, etc. I don't have to add "oh, and be careful because 'data' is a pointer to a pointer", because it's called 'ppData'.

And for the same reason, I don't have to move my eyes out of a line of code in order to understand it. I don't have to search through the code to find if 'data' is a local, parameter, member, or constant. I don't have to move my hand to the mouse so I can hover the pointer over 'data' and then wait for a tooltip (that sometimes never appears) to pop up. So programmers can read and understand the code significantly faster, because they don't waste time searching up and down or waiting.

(If you don't think you waste time searching up and down to work stuff out, find some code you wrote a year ago and haven't looked at
since. Open the file and jump about half way down without reading it.
See how far you can read from this point before you don't know if
something is a member, parameter or local. Now jump to another random
location... This is what we all do all day long when we are single
stepping through someone else's code or trying to understand how to
call their function)

The 'm' prefix also avoids the (IMHO) ugly and wordy "this->" notation, and the inconsistency that it guarantees (even if you are careful you'll usually end up with a mixture of 'this->data' and 'data' in the same class, because nothing enforces a consistent spelling of the name).

'this' notation is intended to resolve ambiguity - but why would anyone deliberately write code that can be ambiguous? Ambiguity will lead to a bug sooner or later. And in some languages 'this' can't be used for static members, so you have to introduce 'special cases' in your coding style. I prefer to have a single simple coding rule that applies everywhere - explicit, unambiguous and consistent.

The last major benefit is with Intellisense and auto-completion. Try using Intellisense on a Windows Form to find an event - you have to scroll through hundreds of mysterious base class methods that you will never need to call to find the events. But if every event had an "e" prefix, they would automatically be listed in a group under "e". Thus, prefixing works to group the members, consts, events, etc in the intellisense list, making it much quicker and easier to find the names you want. (Usually, a method might have around 20-50 values (locals, params, members, consts, events) that are accessible in its scope. But after typing the prefix (I want to use an index now, so I type 'i...'), I am presented with only 2-5 auto-complete options. The 'extra typing' people attribute to prefixes and meaningful names drastically reduces the search space and measurably accelerates development speed)

I'm a lazy programmer, and the above convention saves me a lot of work. I can code faster and I make far fewer mistakes because I know how every variable should be used.


Arguments against

So, what are the cons? Typical arguments against prefixes are:

  • "Prefix schemes are bad/evil". I agree that "m_lpsz" and its ilk are poorly thought out and wholly useless. That's why I'd advise using a well designed notation designed to support your requirements, rather than copying something that is inappropriate for your context. (Use the right tool for the job).

  • "If I change the usage of something I have to rename it". Yes, of course you do, that's what refactoring is all about, and why IDEs have refactoring tools to do this job quickly and painlessly. Even without prefixes, changing the usage of a variable almost certainly means its name ought to be changed.

  • "Prefixes just confuse me". As does every tool until you learn how to use it. Once your brain has become used to the naming patterns, it will filter the information out automatically and you won't really mind that the prefixes are there any more. But you have to use a scheme like this solidly for a week or two before you'll really become "fluent". And that's when a lot of people look at old code and start to wonder how they ever managed without a good prefix scheme.

  • "I can just look at the code to work this stuff out". Yes, but you don't need to waste time looking elsewhere in the code or remembering every little detail of it when the answer is right on the spot your eye is already focussed on.

  • (Some of) that information can be found by just waiting for a tooltip to pop up on my variable. Yes. Where supported, for some types of prefix, when your code compiles cleanly, after a wait, you can read through a description and find the information the prefix would have conveyed instantly. I feel that the prefix is a simpler, more reliable and more efficient approach.

  • "It's more typing". Really? One whole character more? Or is it - with IDE auto-completion tools, it will often reduce typing, because each prefix character narrows the search space significantly. Press "e" and the three events in your class pop up in intellisense. Press "c" and the five constants are listed.

  • "I can use this-> instead of m". Well, yes, you can. But that's just a much uglier and more verbose prefix! Only it carries a far greater risk (especially in teams) because to the compiler it is optional, and therefore its usage is frequently inconsistent. m on the other hand is brief, clear, explicit and not optional, so it's much harder to make mistakes using it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文