构造函数指针更改

时间:2014-05-09 00:05:07

标签: c++

我有一个文件test.cpp

#include <stdio.h>
class B {
    public:
            B() {}
};
class A {
    public:
            A(B *b) {
                    b = b;
            }
            B *b;
};
int main() {
    B b = B();
    A a = A(&b);
    printf("b: %p\n", &b);
    printf("a.b: %p\n", a.b);
}

g++ -Wall -std=c++11 test.cpp下编译并运行,给出结果:

b: 0x7fffc7e9fa6f
a.b: 0x7fffc7e9fb60

我假设指针是直接为构造函数复制的,但是我们发现A类的B实例中保存的值已经改变。

非常感谢任何帮助!

修改 - 关于此问题可能偏离主题的讨论:

我不认为这是一个简单的印刷错误&#34;因为在这种情况下,名称范围错误并不是立即显而易见的。

  • 在隔离环境中创建的程序,旨在演示错误。
  • 例如,在Python中,允许使用这种类变量赋值方法。虽然这些命名方案的优点可以留待辩论,但 是一个合理的混淆基础。
  • 我也在寻找解决这个问题的方法(当然很天真),也无法在网上找到解决方案。

修改编辑 - 很明显,此问题已得到彻底解答,因此无需进一步回复。关闭&#34;偏离主题的原因&#34;然而,如前所述,这是有争议的。

4 个答案:

答案 0 :(得分:7)

b = b中,b的两个出现都是指构造函数参数。该成员未被初始化。

解决方案:

  • 使用初始化列表A(B *b) : b(b) {}
  • 重命名参数,使其不隐藏成员
  • 将会员称为this->b

答案 1 :(得分:3)

A::A()重写为

A(B *b) {
    this->b = b;
}

否则,b中的b = b;个引用相同的变量:参数。

答案 2 :(得分:2)

将A的构造函数更改为

A(B *b) : b(b) {}

答案 3 :(得分:2)

在我写这篇文章时,一个答案已被选为“解决方案”。

问题是一个自我赋值b = b;,源于对两个不同的事物使用相同的名称b - 所以这两个含义混淆了。

选择为解决方案的答案确实包括为不同的事物使用不同的名称的可能性,但是它限制了重命名参数,并且它建议保持被证明是混乱的相同的名字。


由于问题直接源于对不同事物使用相同的名称,因此最直接的解决方案是为不同的事物使用不同的名称。

在手头的情况下,实现这一目标的最简单方法是删除构造函数,以便只剩下数据成员 - 现在具有唯一的名称。

然后是原始代码......

class A {
    public:
            A(B *b) {
                    b = b;
            }
            B *b;
};
例如,

只是......

struct A { B* b; };

原始实例化A a( p );变为A a{ p };

更简单。


有一个构造函数至少意味着类不变的可能性,在这种情况下,限制对数据成员的访问是明智的。

几乎普遍的约定是对非public成员变量使用一些前缀或后缀。例如。 Microsoft使用m前缀,而许多其他使用下划线后缀。 (作为前缀的下划线保留用于全局命名空间中的实现。)

对数据成员使用下划线后缀约定,并用更普遍适用的内存初始化程序替换赋值,原始内容变为如下:

class A
{
private:
    B* b_;

public:
    auto b() const
        -> B*
    { return b_; }

    A( B* b_value )
        : b_( b_value )
    {}
};

如果没有类不变的话,前面显示的简单struct就足够了,而这个更简洁&amp;复杂的代码(或等效的)是安全维护类不变量所需要的。原始代码介于两者之间。对于简单的数据结构案例来说不够简单,但对于has-invariant案例来说不够安全。


重命名参数以使其不隐藏成员是有问题的,因为它保持了简单和安全抽象级别之间的中间位置。这也是将成员称为this->b的想法的问题。并且使用初始化列表A(B *b) : b(b) {}是有问题的,因为它依赖于语言的微妙,因此大多数普通的C ++程序员可能必须至少三思而后才能确保自己的正确性,并且至少有些人会认为它一定是错误的。