24

我正在处理一个使用以下结构的大型代码库

class MyClass
{
public:
  void f(int x);
private:
  int x;
};


void MyClass::f(int x)
{
'
'
  this->x = x;
'
'
}

就个人而言,我一直使用并因此更喜欢这种形式

class MyClass
{
public:
  void f(int x);
private:
  int _x;
};


void MyClass::f(int x)
{
'
'
  _x = x;
'
'
}

我更喜欢后者的原因是它更简洁(更少的代码=更少的潜在错误),而且我不喜欢同时在范围内有多个同名变量,我可以避免它。也就是说,这些天我越来越频繁地看到前一种用法。我不知道第二种方法有什么好处吗?(例如,对编译时间的影响,与模板代码一起使用等...) 任何一种方法的优点是否足够显着,值得对另一种进行重构?我问的原因是,虽然我不喜欢代码中出现的第二种方法,但引入更多错误的工作量和相关风险并不值得重构。

4

15 回答 15

27

您的版本更干净一些,但是当您使用它时,我会:

  1. 避免前导下划线:在有人选择保留名称 _MyField 之前,_x 是可以的。首字母下划线后跟大写字母不允许作为变量名。请参阅:在 C++ 标识符中使用下划线的规则是什么?
  2. 将属性设为私有或受保护:如果可以编译,更改是安全的,并且您将确保使用您的设置器。
  3. this-> 故事有一个用途,例如在模板代码中使字段名称取决于您的类型(可以解决一些查找问题)。

通过使用显式 this-> 修复的名称解析的一个小示例(使用 g++ 3.4.3 测试):

#include <iostream>
#include <ostream>

class A
{
public:
  int g_;
  A() : g_(1) {}
  const char* f() { return __FUNCTION__; }
};

const char* f() { return __FUNCTION__; }
int g_ = -1;

template < typename Base >
struct Derived : public Base
{
  void print_conflicts()
  {
    std::cout << f() << std::endl; // Calls ::f()
    std::cout << this->f() << std::endl; // Calls A::f()
    std::cout << g_ << std::endl; // Prints global g_
    std::cout << this->g_ << std::endl; // Prints A::g_
  }
};

int main(int argc, char* argv[])
{
   Derived< A >().print_conflicts();
   return EXIT_SUCCESS;
}
于 2009-06-29T09:43:47.777 回答
10

字段命名与代码气味无关。正如尼尔所说,现场可见性是这里唯一的代码味道。

有很多关于 C++ 命名约定的文章:

等等

于 2009-06-29T09:47:30.810 回答
4

Microsoft C# 编码标准鼓励使用“this”。它提供了良好的代码清晰度,旨在成为使用 m_ 或 _ 或成员变量中其他任何内容的标准。

老实说,无论如何,我真的不喜欢名称中的下划线,我曾经在我的所有成员前面加上一个“m”。

于 2009-06-29T09:42:42.270 回答
3

很多人使用它是因为在他们的 IDE 中它会弹出当前类的标识符列表。

我知道我在 BCB 做。

我认为您提供的命名冲突示例是一个例外。但是在 Delphi 中,样式指南使用前缀(通常是“a”)作为参数来避免这种情况。

于 2009-06-29T09:49:36.283 回答
3

我个人的感觉是,你不应该与现有的编码约定作斗争。正如 Sutter/Alexandrescu 在他们的《C++ 编码约定》一书中所说:不要为小事而烦恼。任何人都可以阅读其中一个或另一个,无论是否有前导“this->”或“_”或其他任何内容。

但是,命名约定的一致性是您通常想要的,因此在某个范围内(至少是文件范围,当然最好是整个代码库)坚持一种约定被认为是一种好的做法。您提到这种风格在更大的代码库中使用,所以我认为改进另一个约定将是一个坏主意。

毕竟,如果您发现有充分的理由进行更改,请不要手动进行。在最好的情况下,您的 IDE 支持这些类型的“重构”。否则,编写一个脚本来更改它。搜索和替换应该是最后的选择。在任何情况下,您都应该有一个备份(源代码控制)和某种自动化测试工具。否则你不会玩得开心。

于 2009-06-30T08:00:46.893 回答
2

以这种方式使用“this”不是 IMO 代码的味道,而只是个人喜好。因此,它不如与系统中其余代码的一致性重要。如果此代码不一致,您可以更改它以匹配其他代码。如果通过更改它会导致与其余代码的大部分不一致,那是非常糟糕的,我会不理会它。

你永远不想进入打代码网球的位置,有人纯粹为了让它看起来“漂亮”而改变一些东西,只是为了让其他人后来有不同的品味,然后再把它改回来。

于 2009-06-29T11:28:02.840 回答
1

我总是使用m_命名约定。虽然我一般不喜欢“匈牙利符号”,但如果我正在处理班级成员数据,我发现非常清楚地看到它非常有用。此外,我发现在同一范围内使用 2 个相同的变量名太容易出错。

于 2009-06-29T09:39:33.620 回答
0

我同意。我不喜欢这种命名约定——我更喜欢成员变量和局部变量之间有明显区别的命名约定。如果你离开会发生什么this

于 2009-06-29T09:38:10.300 回答
0

class MyClass{
public:  
  int x;  
  void f(int xval);
};
//
void MyClass::f(int xval){  
  x = xval;
}
于 2009-06-29T09:39:41.560 回答
0

在我看来,往往会给代码增加混乱,所以我倾向于使用不同的变量名(根据约定,它可能是一个下划线、m_等等)。

于 2009-06-29T09:42:04.597 回答
0

在 C++ 中,使用初始化器在构造时初始化成员更为正常。

为此,您必须使用与成员变量名称不同的名称。

因此,即使我会Foo(int x) { this.x = x; }在 Java 中使用,我也不会在 C++ 中使用。

真正的味道可能是没有使用初始化器和方法,它们除了改变成员变量之外什么都不做,而不是使用this -> x它本身。

任何人都知道为什么在与初始化程序一起使用时,在我去过的每个 C++ 商店中,对成员变量的构造函数参数使用不同的名称是普遍做法吗?是否有一些不支持它的 C++ 编译器?

于 2009-06-29T09:50:23.270 回答
0
class MyClass
{
public:
  int m_x;
  void f(int p_x);
};


void MyClass::f(int p_x)
{
  m_x = p_x;
}

...是我使用范围前缀的首选方式。m_ 代表成员,p_ 代表参数(有些使用 a_ 代替参数),g_ 代表全局,有时 l_ 代表局部,如果它有助于提高可读性。

如果您有两个变量应该具有相同的名称,那么这会很有帮助,并且避免为了避免重新定义而必须对其含义进行一些随机变化。或者更糟糕的是,可怕的“x2、x3、x4 等”......

于 2009-06-29T09:51:08.090 回答
0

我不喜欢使用“this”,因为它是返祖的。如果您正在使用古老的 C 语言(还记得 C 吗?)进行编程,并且想要模仿 OOP 的某些特征,您可以创建一个包含多个成员的结构(这些成员类似于您的对象的属性)并创建一个集合的函数都将指向该结构的指针作为其第一个参数(这些类似于该对象的方法)。

(我认为这种 typedef 语法是正确的,但已经有一段时间了......)

typedef struct _myclass
{
   int _x;
} MyClass;

void f(MyClass this, int x)
{
   this->_x = x;
}

事实上,我相信旧的 C++ 编译器实际上会将您的代码编译为上述形式,然后将其传递给 C 编译器。换句话说——C++ 在某种程度上只是语法糖。所以我不知道为什么有人会想用 C++ 编程并在代码中明确使用“this”——也许它是“语法 Nutrisweet”

于 2009-06-29T15:03:38.143 回答
0

今天,大多数 IDE 编辑器为变量着色以指示局部变量的类成员。因此,IMO,为了可读性,既不需要前缀也不需要'this->'。

于 2015-06-29T12:01:50.667 回答
-2

如果您对命名约定有疑问,可以尝试使用以下内容。

class tea
{
public:
    int cup;
    int spoon;
    tea(int cups, int spoons);
};

或者

class tea
{
public:
    int cup;
    int spoon;
    tea(int drink, int sugar);
};

我想你明白了。它基本上是在逻辑意义上将变量命名为不同但“相同”。我希望它有所帮助。

于 2009-06-29T10:01:40.657 回答