【问题标题】:Access members of a class within a different class in c++在 c++ 中访问不同类中的类的成员
【发布时间】:2016-06-03 02:28:52
【问题描述】:

我正在尝试理解使用 c++ 进行的面向对象编程。下面是一个最小的例子,它的结果不是我天真期望的:

#include <iostream>

class B {
public:
      B (int val) : val(val) {;}

      int get_val() { return val; }
      int set_val(int a) { val = a; }

private:
      int val;
};

class A {
public:
      A (B b) : b(b) {;}
      B get_b() { return b; }

private:
      B b;
};

int main(){
    B b_main(5);
    std::cout << b_main.get_val() << std::endl; // Prints 5, which makes sense

    A a_main(b_main);
    std::cout << a_main.get_b().get_val() << std::endl; // Prints 5, which makes sense

    a_main.get_b().set_val(2);
    std::cout << a_main.get_b().get_val() << std::endl; // Why does this not print 2?

    return 0;
}

最后一个 cout 语句对我来说没有意义。在倒数第二行中,我将对象的值设置为 2,为什么不打印 2?查看 Stack Exchange 上的一些类似问题,我发现了一些建议让 A 和 B 成为彼此的朋友。我尝试在 B 类中添加 friend class A,在 A 类中添加 friend class B,但这不起作用。在我的理解中,添加朋友语句应该是不必要的,因为我在 A 类中有 get_b() 方法。我找到了一些建议,尝试通过引用 A 的构造函数来传递 B 类型的对象:A (B&amp; b) : b(b) {;} 但这确实也不行。

谁能向我解释为什么程序没有产生预期的结果以及如何获得预期的结果(即最后一个 cout 语句打印 2)?

注意:我还尝试了以下方法。我将 A 类的私有变量 b 设为 public:

#include <iostream>

class B {
public:
    B (int val) : val(val) {;}
    int get_val() { return val; }
    int set_val(int a) { val = a; }

private:
    int val;
};

class A {
public:
  A (B b) : b(b) {;}
  B b; // This is now public
  //B get_b() { return b; } // This is no longer needed

private:
};


int main(){
    B bmain(5);
    std::cout << bmain.get_val() << std::endl;

    A amain(bmain);
    std::cout << amain.b.get_val() << std::endl;

    amain.b.set_val(2);
    std::cout << amain.b.get_val() << std::endl; // Works!

    return 0;
}

现在我得到了想要的结果。与第一个代码 sn-p 相比,这是应该如何实现代码的吗?我想在第一个代码 sn-p 中使用get_b() 方法,但如果这不是解决此问题的正确方法,请告诉我。

【问题讨论】:

    标签: c++ oop object member


    【解决方案1】:

    倒数第二行,我将对象的值设置为2,为什么不打印2?

    因为您使用get_b() 方法返回a_mainB 对象的副本。发生的情况是,a_main 中的 b 变量被复制,即创建了另一个与 b 成员相同的类 B 的对象,并将其返回给调用者。然后,修改新的B 对象。但它与a_main 中的原始b 无关。这与可见性和成员访问权限无关。

    但是,在第二个示例中,您在 a_main 中公开了 b 成员,并直接对该对象进行操作而不复制它,因此是成功的结果。 public 修饰符改变的是它允许您直接访问b 对象,因此效果。

    我找到了一些建议,尝试通过引用 A:A (B&amp; b) : b(b) {;} 的构造函数来传递 B 类型的对象,但这也不起作用。

    那是行不通的。这样做时会发生什么,A::b 使用通过引用传递的值进行初始化,true。但是引用只会导致没有额外的b 副本传递给正在生成的构造函数。此引用不会在传递给构造函数的bA::b 之间创建链接。可以这么说,它在另一端。

    顺便说一句,A (B&amp; b) : b(b) {;} 认为 c'tor 参数名称与成员名称相同是一个不好的做法。将它们命名相似是个好主意,但仍然添加例如下划线:A (B&amp; _b) : b(_b) {;}

    如果您想在第一个 sn-p 中达到相同的结果,请将 reference 返回到 b,如下所示:

      B& get_b() { return b; }
    

    不过,这是不可取的,因为您公开了 A 类的私有成员,只是为了允许 A 的客户修改该成员的某个属性。最好在A 中提供一种方法来设置A::bval 属性,而无需完全访问A::b

    一定看到这个:What's the difference between passing by reference vs. passing by value?

    也许这个:Java and C++ pass by value and pass by reference

    因为我觉得您来自 Java,并且默认情况下期望在 C++ 中通过引用传递。

    【讨论】:

      【解决方案2】:

      为了完整起见,您可以将此问题作为一个 C 编程问题来解决,而不是使用 C++ 编程中的所有花哨的引用。当你从a_main得到b_main时,返回的对象不会占用相同的内存地址。

      #include <iostream>
      
      class B {
      
      public:
      
        B (int val) : val(val) {;}
      
        int get_val() { return val; }
        int set_val(int a) { val = a; }
      
      private:
      
        int val;
      
      };
      
      class A {
      
      public:
      
        A (B b) : b(b) {;}
        B get_b() { return b; }
      
      private:
      
        B b;
      
      };
      
      
      int main(){
      
        B b_main(5);
        B* addrb = &b_main;
         std::cout << b_main.get_val() << std::endl; // Prints 5, which makes sense
          std::cout<<"Address of b_main: "<<addrb<<std::endl;
        A a_main(b_main);
        B bt = a_main.get_b();
        addrb = &(bt);
        std::cout << a_main.get_b().get_val() << std::endl; // Prints 5, which makes sense
          std::cout<<"Address of a_main.get_b(): "<<addrb<<std::endl;
        a_main.get_b().set_val(2);
        std::cout << a_main.get_b().get_val() << std::endl; // Why does this not print 2?
      
        return 0;
      
      }
      

      注意新 cout 语句的地址差异。解决此问题的一种方法是返回指针而不是 b 本身。即

      #include <iostream>
      
      class B {
      
      public:
      
        B (int val) : val(val) {;}
      
        int get_val() { return val; }
        int set_val(int a) { val = a; }
      
      private:
      
        int val;
      
      };
      
      class A {
      
      public:
      
        A (B b) : b(b) {;}
        B* get_b() { return &b; }
      
      private:
      
        B b;
      
      };
      
      
      int main(){
      
        B b_main(5);
        //B* addrb = &b_main;
         std::cout << b_main.get_val() << std::endl; // Prints 5, which makes sense
          //std::cout<<"Address of b_main: "<<addrb<<std::endl;
        A a_main(b_main);
        //B bt = a_main.get_b();
        //addrb = &(bt);
        std::cout << a_main.get_b()->get_val() << std::endl; // Prints 5, which makes sense
          //std::cout<<"Address of a_main.get_b(): "<<addrb<<std::endl;
        a_main.get_b()->set_val(2);
        std::cout << a_main.get_b()->get_val() << std::endl; // Why does this not print 2?
      
        return 0;
      
      }
      

      【讨论】:

        【解决方案3】:

        get_b 返回私有变量 b 的副本,而不是实际变量。如果您希望能够访问它,则需要返回对 b 的引用,以便可以操作返回的值。您的 get_b 定义应如下所示:

        B& get_b() { return b; }
        

        如果这是您期望做的。然而,这通常不是一个理想的解决方案。如果您要主动更改 b 的值,您应该编写一个 set_b 函数来操作该变量。如果您确实经常使用该变量,对其进行读取和写入值,您应该将其公开以便快速访问。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-05-29
          • 2011-11-03
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多