C++ 构造函数指针更改

C++ 构造函数指针更改,c++,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

我有一个文件
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);
}
我假设指针是直接为构造函数复制的,但是我们看到
B
类的
A
实例中保存的值发生了变化

任何帮助都将不胜感激

编辑——关于这个问题可能离题的讨论:

我不认为这是一个“简单的印刷错误”,因为在这种情况下,名称范围错误并不明显

  • 这是一个在隔离环境中创建的程序,旨在演示错误
  • 例如,在Python中,这样的类变量赋值方法是允许的。虽然这种命名方案的优点可以留待讨论,但这里有一个合理的混乱基础
  • 我也在寻找这个问题的解决方案(承认很幼稚),而且在网上也很难找到解决方案

编辑——很明显,这个问题已经得到了彻底的回答,因此不需要进一步的回答。然而,如前所述,关闭“主题外”的原因仍有争议。

将A的构造函数更改为

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

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

解决方案:

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

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

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

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

      问题是,自我分配
      b=b
      ,源于对两个不同的事物使用相同的名称
      b
      ,从而混淆了这两个含义

      selected as solution答案确实包括为不同的事物使用不同名称的可能性,但它将此限制为重命名参数,并建议将proven保持为相同的名称


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

      在本例中,实现这一点的最简单方法是删除构造函数,这样就只剩下数据成员了——现在它的名称是唯一的

      然后原始代码

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

      struct A { B* b; };
      
      和原始实例化
      A(p)
      变成
      A{p}

      简单得多


      拥有一个构造函数至少意味着类不变量的可能性,在这种情况下,谨慎地限制对数据成员的访问

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

      使用数据成员的下划线后缀约定,并使用更普遍适用的内存初始值设定项替换赋值,原始值如下所示:

      class A
      {
      private:
          B* b_;
      
      public:
          auto b() const
              -> B*
          { return b_; }
      
          A( B* b_value )
              : b_( b_value )
          {}
      };
      
      如果没有类不变量,前面显示的简单的
      struct
      就足够了,而这个更详细、更复杂的代码(或等效代码)是安全维护类不变量所需要的。原始代码介于两者之间。对于简单数据结构的情况来说不够简单,但是对于has不变的情况来说不够安全



      重命名参数以便不隐藏成员是有问题的,因为它保持了简单抽象级别和安全抽象级别之间的中间位置。这也是将成员称为
      this->b
      的想法的一个问题。使用初始化器列表<代码> A(b*b):b(b){} //>是有问题的,因为它依赖于语言的微妙性,因此大多数普通C++程序员可能必须至少考虑两次以确保其正确性,因此至少一些人会认为它是错误的。(虽然我会猜到,
      -Wall
      抓住了这一点)。再想一想,没关系。警告这一点的是叮当声。作为旁白,
      bb;
      aaa(&B);
      同样有效,而
      %p
      printf
      的参数应该是
      void*
      (这不会发生在
      std::cout
      上)。语句
      b=b;
      是一种自赋值。对于有效的指针值,它没有任何效果,除了可能使用纳秒或两纳秒。命名不明确的变量的危险:):)可能提到我们建议重命名。@Cheersandhth.-Alf:请自言自语。如果有人需要我的推荐,我会推荐一个初始化列表。重命名该成员以使其不被参数隐藏的好理由包括(1)更清楚名称指的是什么,(2)这是几乎普遍的约定。这并不排除使用初始值设定项列表。我会两者都做。@Cheers-Sandhth.-Alf:(1)它同样清楚,除非构造函数太复杂;(2) 我必须和你生活在不同的世界里。(而且,如果您确实重命名了它,在初始化器列表中会有意外地对其进行自初始化的危险。)谢谢——我想我在Python模式下停留了一秒钟这里=P而且,我忘了我可能还应该使用
      \u b
      来规避这类问题(私有范围,yadda yadda yadda等)。
      class A
      {
      private:
          B* b_;
      
      public:
          auto b() const
              -> B*
          { return b_; }
      
          A( B* b_value )
              : b_( b_value )
          {}
      };