为了账号安全,请及时绑定邮箱和手机立即绑定

C++ 和 Java 中引用赋值的区别

C++ 和 Java 中引用赋值的区别

慕妹3242003 2023-04-19 16:05:06
我已经学习 C++ 两个星期了。在 Java 中,如果我们有两个属于同一个类的不同对象,并且如果我们将一个对象的引用分配给另一个对象的另一个引用,那么它们就指向同一个对象。之后,通过一个引用更改数据成员也会更改另一个引用中的数据成员。我的问题是:在 C++ 中不是也一样吗?我对复制构造函数和赋值运算符有点困惑。他们两个都做深拷贝。没有他们,据我所知,我们只能做浅拷贝。我也有一个代码片段。#include <iostream>using namespace std;class Test{    int x;    int &ref;    public:        Test(int i):x(i), ref(x) {}        void print() { cout << ref;}        void setX(int i) {x = i;}        Test &operator = (const Test &t) {x = t.x; return *this;}};int main(){    Test t1(10);    Test t2(20);    t2 = t1;    t1.setX(40);    t2.print();  // This will print 10    cout << "\n\n";    t1.print();  // This will print 40    return 0;}
查看完整描述

5 回答

?
莫回无

TA贡献1865条经验 获得超7个赞

看你的陈述:

我已经学习 C++ 两个星期了。

祝贺你向前迈出了一大步。:-)

在 Java 中,如果我们有两个属于同一个类的不同对象,并且如果我们将一个对象的引用分配给另一个对象的另一个引用,那么它们就指向同一个对象。

这里的区别在于引用的定义。Java 称为引用的 C++ 将称为指针(因为它们始终指的是持续超出当前范围的动态分配的对象)。尽管 Java 引用是智能的,并且当所有“Java 引用”都消失时对象被垃圾收集。因此,Java 引用更等同于std::shared_ptr<>.

// Java                       // C++

String s = new String("S");   std::shared_ptr<std::string> s = new std::string("S");

C++ 自动对象更像是 Java 的“值类型”(int/char/float)。不同之处在于,在 C++ 中,任何类型都可以充当“值类型”。


// Java                       // C++

int      x = 12;              int    x = 12;

                              Test   t1(10);  // acts like an int value

                                              // except destructor called

                                              // when it goes out of scope.

之后,通过一个引用更改数据成员也会更改另一个引用中的数据成员。

是的,基本上。但是你必须注意你的措辞。如果该成员是一个引用,那么您不会更改此成员(因为引用永远不会更改它们在 C++ 中引用的内容),您正在更改引用所引用的对象。

// Java

class MyType  // forgive my syntax.

{

     public MyType() {x = new MyOtherType(); y = x}

     public MyOtherType x;

     public MyOtherType y;

};


MyType       a = new MyType();

MyOtherType  b = new MyOtherType();


a.x = b;


// Here both a.x and b refer to the same object.

// While a.y will refer to the original value.

// That is not how C++ will work (if you are thinking reference like

// like your description).



// C++

class MyType

{

     public:

         MyType() : x(), y(x) {}

         MyOtherType  x;

         MyOtherType& y;

          // Note   ^

};



MyType       a;

MyOtherType  b;


a.x = b;

我的问题是:在 C++ 中不是也一样吗?

不,因为引用不是对象。您需要将 Java 引用视为等同于 C++ std::shared_ptr。这就是我们在 C++ 中管理动态分配内存的方式。

我对复制构造函数和赋值运算符有点困惑。他们两个都做深拷贝。没有他们,据我所知,我们只能做浅拷贝。我也有一个代码片段。

基本上是真的。它基本上允许您定义在将一个对象复制到另一个对象时赋值如何正确工作。

让我们评论代码。
您描述的输出是我期望的输出。

#include <iostream>
using namespace std;          // Please stop doing this.

测试

class Test

{

    int x;

    int &ref;


    public:

        Test(int i)

            : x(i)            // Set this->x = i

                              // Note X is an automatic object.

            , ref(x)          // Set this->ref as an alias for this->x

                              // Note1: This can never be undone.

                              // Note2: Changing ref will always change x

                              //        conversely changing x will be

                              //        visible via ref.

                              // Note3: ref can **not** be made to reference

                              //        another object.

        {}

        void print()

        {

            cout << ref;      // Prints what this->ref is a reference too:

                              // Looking at the constructor its this->x

        }

        void setX(int i)

        {

            x = i;            // Modifies this->x

                              // This is visible via this->ref

        }

        Test &operator = (const Test &t)

        {

            x = t.x;          // Copies the value from t.x into this->x

                              // Note1: this->ref and t.ref continue to refer

                              //        to their origin objects.

                              // Note2: since this->x has changed

                              //        this change is visible in this->ref

            return *this;

        }

};

主要的

int main()

{

    Test t1(10);                  // t1.x = 10 t1.ref = t1.x

    Test t2(20);                  // t2.x = 20 t2.ref = t2.x

    t2 = t1;                      // Assignment operator above called.

                                  // t2.x = 10 t2.ref = t2.x


    t1.setX(40);                  // Call the setX method on t1

                                  // t1.x = 40 t1.ref = t1.x


    t2.print();                   // Call the print method on t2

                                  // This prints t2.ref to the output.

                                  // t2.ref is a reference to t2.x

                                  // So we print the value t2.x

                                  // Should print 10


    cout << "\n\n";


    t1.print();                   // Call the print method on t1

                                  // This prints t1.ref to the output.

                                  // t1.ref is a reference to t1.x

                                  // So we print the value t1.x

                                  // Should print 40


    return 0;

}


查看完整回答
反对 回复 2023-04-19
?
富国沪深

TA贡献1790条经验 获得超9个赞

在 Java 中,如果我们有两个属于同一个类的不同对象,并且如果我们将一个对象的引用分配给另一个对象的另一个引用,那么它们就指向同一个对象。

我的问题是:在 C++ 中不是也一样吗?

这根本不一样。在 C++ 中,不能重新分配引用以引用另一个对象。他们在整个生命周期中都指代同一个对象。将赋值操作应用于引用时,将对引用的对象进行赋值。

请注意,Java 没有显式引用。所有类类型变量都是引用,原始变量是值对象。C++是不同的。您必须明确指定变量是引用还是对象,并且您可以拥有类类型的值对象以及对基本类型的引用。

在某些方面,Java 引用比 C++ 引用更类似于 C++ 指针。特别是,指针可以为空,并且可以赋值指向别处,就像 Java 引用一样。

// Java

int i = 0;             // not a reference

i = some_int;          // i is modified

Test tref = null;      // a reference

tref = t;              // reference is modified

tref = other_t;        // reference is modified; t is not modified


// C++

Test t;                // not a reference

Test& tref = t;        // a reference

t = other_t;           // t is modified

tref = other_t;        // t is modified; reference is not


Test* tptr = nullptr;  // a pointer (not a reference)

tptr = &t;             // tptr is modified

*tptr = other_t;       // t is modified

tptr = other_t;        // tptr is modified; t is not modified


查看完整回答
反对 回复 2023-04-19
?
qq_花开花谢_0

TA贡献1835条经验 获得超6个赞

创建t2实例时,t2.ref初始化为引用t2.x

Test(int i):x(i), ref(x) {}

后者,

t2 = t1;

不会改变t2.ref所指的内容(因为它是无效的:在 C++ 中,引用不能绑定到新的引用对象):

Test &operator = (const Test &t) {x = t.x; return *this;}

t2.ref仍然指的是t2.x,您永远不会改变其价值:

t1.setX(40);



查看完整回答
反对 回复 2023-04-19
?
有只小跳蛙

TA贡献1824条经验 获得超8个赞

在他关于 C++ 的书中,Stroustrup 对此进行了解释:


请注意,这两个对象是独立的。我们可以改变 y 的值而不影响 x 的值。例如 x=99 不会改变 y 的值。与 Java、C# 和其他语言不同,但与 C 一样,这适用于所有类型,而不仅仅是整数。如果我们希望不同的对象引用相同的(共享的)值,我们必须这样说。我们可以使用指针:


int x = 2;

int y = 3;

int∗ p = &x;

int∗ q = &y; // now p!=q and *p!=*q

p = q; // p becomes &y; now p==q, so (obviously)*p == *q

//img1.sycdn.imooc.com//643fa10d0001a30105160103.jpg

我随意选择了 88 和 92 作为 int 的地址。同样,我们可以看到被赋值对象从被赋值对象获取值,产生两个具有相同值的独立对象(此处为指针)。也就是说,p=q 给出 p==q。p=q之后,两个指针都指向y。引用和指针都引用/指向一个对象,并且都在内存中表示为机器地址。但是,使用它们的语言规则不同。对引用的赋值不会改变引用所指的内容,而是将其赋值给被引用的对象:


int x = 2;

int y = 3;

int& r = x; // r refers to x

int& r2 = y; // now r2 refers to y

r = r2; // read through r2, write through r: x becomes 3


查看完整回答
反对 回复 2023-04-19
?
SMILET

TA贡献1796条经验 获得超4个赞

在 Java 中,您有两种对象:那些始终按值传递的对象,它们完全是本机数据类型:int、long、double,...和非本机对象,隐式继承自 ,它们存储在变量中Object作为引用并在赋值时传递(例如传递给其他变量或函数参数)。

在 C++ 中,任何数据类型都可以显示两种类型的行为,按值传递或按引用传递。而在 C++ 中,您必须明确说明。

在 C++ 中,您可以通过三种方式传递对象,而不仅仅是两种:

  • 按价值

  • 引用

  • 通过指针

与 C++ 引用相比,Java 引用更接近于 C++ 指针:您可以将指针重新分配给不同的对象,但不能为引用这样做。它有助于将 C++ 引用视为具体对象的别名(另一个名称),忽略在某些情况下这可能真的适用(然后根本没有分配内存)而在其他情况下(尤其是函数参数)则不然。

现在准确地说:即使在 Java 中,每个变量都会被复制,甚至是引用变量。但是对于后者,复制的只是引用本身,而不是对象!所以要显示相似之处:

// Java                         // C++


int n = 10;                     int n = 10;

String s = "hello";             std::string* s = new std::string("hello");


f(n, s);                        f(n, s);

voidf(int n, String s)          void f(int n, std::string* s)

{                               {

    n = 12;                          n = 12; // in both cases, only local variable changed

    s += "ads";                     *s += "ads";

                                //  ^ solely: special syntax for pointers!

    s = "world";                    s = new std::string("world");

   // now s refers/points to another object in both cases

}                               }

到目前为止应该知道......老实说,上面简化了太多:在 Java 中,我们有自动垃圾收集,在 C++ 中,我们没有。所以实际上,上面在 C++ 中创建的字符串对象再也没有被清理过,我们有内存泄漏!现代 C++ 引入了智能指针来解决这个问题,同时考虑 Java 中的垃圾回收,C++ 代码可能看起来像:


int n;

std::shared_ptr<std::string> s = std::make_shared("hello");

f(n, s);


void f(int n, std::shared_ptr<std::string> s)

{

    n = 12;

    *s += "ads";

    s = std::make_shared("world");

}

std::shared_ptrJava中的所有引用计数都隐藏在基类中的哪里Object......


与 Java 不同的是,您也可以按值传递字符串:


void f(int, std::string s);

现在字符串将被复制到局部变量中,就像 int 一样。唯独:怎么???


这就是复制构造函数发挥作用的地方。如果您愿意,它是如何创建该副本的秘诀,即如何将内部表示复制到新对象中。对于 a std::string,这将是一些内部的、动态分配的数组,其中包含字符串的内容、其当前容量(即数组的总长度)和大小(实际占用了多少数组)。


最后是 C++ 参考:


void f(int, std::string& s);

在这种情况下, s 将始终引用分配给该函数的对象,您不能更改引用(至少在法律上是这样,在某些情况下您可以使用一些非常肮脏的黑客...),只能更改引用的对象。


实际上,在 C++ 中,问题更复杂:


在 Java 中,您可以获得自动内存管理,有一个垃圾收集器选择所有不再被引用的对象。这种事情在 C++ 中不存在,我们需要手动清理。实际上,我在上面的示例中创建的字符串根本没有被清理,所以我们发生了内存泄漏!!!


查看完整回答
反对 回复 2023-04-19
  • 5 回答
  • 0 关注
  • 105 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信