【问题标题】:Conflicts between member names and constructor argument names [duplicate]成员名称和构造函数参数名称之间的冲突[重复]
【发布时间】:2012-10-01 03:19:40
【问题描述】:

可能重复:
Members vs method arguments access in C++

我的班级有一些成员,例如 xywidthheight。在它的构造函数中,我不会这样做:

A::A(int x, int y, int width, int height)
{
    x = x;
    y = y;
    width = width;
    height = height;
}

这真的没有意义,当使用 g++ 编译时,xywidthheight 会变成奇怪的值(例如 -1405737648)。

解决这些命名冲突的最佳方法是什么?

【问题讨论】:

  • 呃,将a 附加到参数名称?
  • A::A (int xa, int ya, int widtha, int heighta)
  • 如果要优雅,前置后缀a,形成英文不定冠词:anX, aY, aWidth, aHaight等。显然不能使用同一个标识符在同一范围内引用两个不同的变量。看我的回答。
  • 嗯?我只是选择了“a”,因为它是我们字母表的第一个字母,我附加了它而不是前置它,因为它看起来更清晰。
  • 我更喜欢给成员变量名称加上一个尾随下划线。即int x_;

标签: c++ class arguments naming-conventions


【解决方案1】:

您可以使用相同名称的初始化列表:

A::A(int x, int y, int width, int height) :
    x(x),
    y(y),
    width(width),
    height(height)
{
}

如果您不想使用相同的名称,另一种方法是使用不同的名称。我想到了一些匈牙利符号的变化(我可能会对此感到讨厌):

//data members
int x_;
int y_;
int width_;
int height_;
//constructor
A::A(int x, int y, int width, int height) :
    x_(x),
    y_(y),
    width_(width),
    height_(height)
{
}

但是第一个建议没有错。

【讨论】:

  • 17.4.3.1.2/1:以下划线开头的每个名称都保留给实现,用作全局命名空间中的名称。
  • @JohnDibling 为什么我认为这只适用于宏?哦,好吧......当我回顾我写的旧代码时,我想还有一件事我会讨厌......:D
  • 匈牙利符号有什么问题? ;) 虽然,这实际上只是一个前缀名称,而不是匈牙利符号的变体。
  • @JohnDibling - 17.4.3.1.2 是“全局名称”。数据成员不是全局名称。尽管如此,我不会以下划线开头数据成员,只是因为。
  • @DavidHammen:我不认为那是什么意思。我认为该部分意味着 any 任何范围内以下划线开头的名称都由实现保留,实现将使用它作为全局名称。
【解决方案2】:

如果您必须在构造函数中使用赋值(而不是使用初始化器列表,这是首选),解决此问题的特定模式是使用this 指针,如下所示:

this->a = a;

【讨论】:

    【解决方案3】:

    如果可能的话,最好通过初始化列表设置数据成员,在这种情况下,隐藏成员名称的参数没有问题。另一种选择是在构造函数的主体中使用this->foo = foo;。 setter 也存在类似的问题,但现在您不能使用初始化列表解决方案。你被this->foo = foo; 卡住了——或者只是为参数和成员使用不同的名称。

    有些人真的很讨厌影响数据成员的论点;多个编码标准明确表示永远不要这样做。其他人认为这种阴影,至少对于构造函数和设置者来说,是猫的喵喵叫。我记得读过一两个编码标准(但我不记得是哪个)将这种阴影指定为“应该”(但不是“应该”)的做法。

    最后一个选择是在函数声明中使用阴影,以便向读者提示函数的作用,但在实现中使用不同的名称。

    更新:什么是“阴影”?

    #include <iostream>
    
    void printi (int i) { std::cout << "i=" << i << "\n"; }
    
    int i = 21; 
    
    int main () {
       printi (i);
       int i = 42; 
       printi (i);
       for (int i = 0; i < 3; ++i) {
          printi (i);
          int i = 10; 
          printi (i);
       }
       printi (i);
    }
    

    iint i=10 的最内层声明遮蔽了在 for 语句中声明的变量 i,进而遮蔽了在函数范围内声明的变量 i,进而遮蔽了全局变量i.

    在手头的问题中,A 类的非默认构造函数的参数 xywidthheight 隐藏了与这些参数同名的成员数据。

    您的width=width; 什么也没做,因为参数width 隐藏(隐藏)了数据成员width。当您有两个或多个在不同范围内声明的具有相同名称的变量时,获胜者始终是具有最内层范围的名称。通常,获胜的总是具有最内部范围的名称。

    【讨论】:

    • 您究竟将什么称为“阴影”?
    • @Bane - 什么是“阴影”? Shadowing:(1)用于测试 CS 101 学生是否理解范围界定的酷刑装置。 (2) 当在某个范围声明的变量与在某个外部范围声明的变量同名时。 (3) 你对构造函数做了什么。 (4) 使用-Wshadow 编译时发现的潜在问题。 (5) 查看我的更新答案。
    【解决方案4】:

    虽然您可以通过使用构造函数的初始化列表来避免该问题,但我建议遵循命名数据成员的约定,例如,结尾的_ 或开头的m_。否则你很可能会发生名称冲突,特别是如果你有名称为 xy 的成员。

    class A
    {
        public:
    
        A(int x, int y, int width, int height) : x_(x), y_(y), with_(width), height_(height) {}
    
        int x_;
        int y_;
        int width_;
        int height_;
    };
    

    【讨论】:

    • 我经常看到这个(尾随下划线)约定——你知道它的起源吗?感谢您的任何提示。
    • this-> 也是消除本地范围歧义的好方法。
    【解决方案5】:

    您可以只更改构造函数参数的名称。当你写

    A::A(int x, int y, int width, int height)
    {
        x = x;
        y = y;
        width = width;
        height = height;
    }
    

    然后您将 构造函数的参数分配给自己,而实际的实例变量未初始化,这就是您获得虚假值的原因。

    我建议(并广泛使用)的一般解决方案是更改构造函数方法的参数名称:

    A::A(int x_initial, int y_initial, int width_initial, int height_initial)
    {
        x = x_initial;
        y = y_initial;
        width = width_initial;
        height = height_initial;
    }
    

    【讨论】:

    • 是的,这就是我所做的,但我没有添加“_initial”,而是附加了“a”。
    • @Bane 那有什么问题?
    • 嗯,这似乎是一个“丑陋”的解决方案,我认为它表明我对构造函数的理解有问题......
    • @Bane 你唯一缺少的是编译器不智能。
    猜你喜欢
    • 2011-04-15
    • 1970-01-01
    • 1970-01-01
    • 2019-11-23
    • 1970-01-01
    • 2021-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多