成员名称和构造函数参数名称之间的冲突[重复]

Posted

技术标签:

【中文标题】成员名称和构造函数参数名称之间的冲突[重复]【英文标题】: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_; 【参考方案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 你唯一缺少的是编译器不智能。

以上是关于成员名称和构造函数参数名称之间的冲突[重复]的主要内容,如果未能解决你的问题,请参考以下文章

使用与 C++ 标准允许的成员变量相同的名称为构造函数参数初始化成员变量? [复制]

拷贝函数和构造函数

常见的构造函数类型

构造函数与默认构造函数

constructor&object 的联系与对比

Kotlin类与对象 ② ( 主构造函数 | 主构造函数定义临时变量 | 主构造函数中定义成员属性 | 次构造函数 | 构造函数默认参数 )