我试图用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的对象:{{1}但这也不起作用。
有人可以向我解释为什么程序没有产生预期的结果,以及如何获得所需的结果(也就是说,最后一个cout语句打印2)?
注意:我还尝试了以下内容。我将A类的私有变量b设为公共:
A (B& b) : b(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 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;
}
方法,但如果这不是正确的解决方法,请告诉我。
答案 0 :(得分:3)
在倒数第二行,我将对象的值设置为2,那为什么不打印2?
因为您使用B
方法在a_main
中返回get_b()
对象的副本。发生的情况是复制b
中的a_main
变量,即创建与B
成员相同的另一个类b
的对象,并返回给调用者。然后,修改新的B
对象。但它与b
中的原始a_main
无关。这与可见性和成员访问几乎没有关系。
但是,在第二个示例中,您在b
中公开a_main
成员并直接对该对象进行操作而不复制它,从而获得成功结果。 public
修饰符更改的内容是它允许您直接访问b
对象,从而产生效果。
我找到了一些建议,尝试通过引用
A
A (B& b) : b(b) {;}
的构造函数来传递类型B的对象,但这也不起作用。
那不会起作用。当你这样做时会发生的是A::b
使用引用传递的值初始化,为true。但是引用只会导致没有传递给正在构造函数的b
的附加副本。此引用不会在传递给构造函数的b
和A::b
之间创建链接。它可以说是另一端。
顺便说一句,A (B& b) : b(b) {;}
c&#tor参数名称与成员名称相同是不良做法。让它们以相似的方式命名是一个好主意,但仍然可以添加例如下划线:A (B& _b) : b(_b) {;}
如果您想在第一个代码段中获得相同的结果,请将引用返回b
,如下所示:
B& get_b() { return b; }
但是,这是不可取的,因为您公开了类A
的私有成员只是为了允许A
的客户端修改该成员的某个属性。最好在A
中提供一种方法来设置val
的{{1}}属性,而不必提供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 ++中默认使用pass-by-reference。
答案 1 :(得分:1)
get_b返回私有变量b的副本,而不是实际变量。如果您希望能够访问它,则需要返回对b的引用,以便可以操作返回的值。你的get_b定义应如下所示:
B& get_b() { return b; }
如果这是你期望的。但是,这通常不是理想的解决方案。如果您要主动更改b的值,则应编写set_b函数来操作变量。如果你真的正在使用变量,读取和写入值,你应该公开它以便快速访问。
答案 2 :(得分:0)
为了完整起见,您可以将此问题解决为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;
}