简体   繁体   English

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

[英]Difference between reference assignment in C++ and Java

I have been learning C++ for two weeks.我已经学习 C++ 两个星期了。 In Java, if we have two different objects of the same class, and if we assign the reference of one object to the other reference of the other object, then they refer to the same object.在Java中,如果我们有同一个类的两个不同对象,并且如果我们将一个对象的引用分配给另一个对象的另一个引用,那么它们引用的是同一个对象。 Afterward, changing a data member by one reference would change the data member in the other reference as well.之后,通过一个引用更改数据成员也会更改另一个引用中的数据成员。 My question is: Isn't it the same in C++ too?我的问题是:在 C++ 中不也是一样吗? I got a bit confused about Copy constructors and assignment operators.我对复制构造函数和赋值运算符有点困惑。 Both of them do deep copy.他们都做深拷贝。 Without them, we can only do shallow copy as far as I know.没有它们,据我所知,我们只能做浅拷贝。 I have a code snippet too.我也有一个代码片段。

#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;
}

In Java, if we have two different objects of the same class, and if we assign the reference of one object to the other reference of the other object, then they refer to the same object.在Java中,如果我们有同一个类的两个不同对象,并且如果我们将一个对象的引用分配给另一个对象的另一个引用,那么它们引用的是同一个对象。

My question is: Isn't it the same in C++ too?我的问题是:在 C++ 中不也是一样吗?

It isn't the same at all.根本不一样。 In C++, references cannot be reassigned to refer to another object.在 C++ 中,不能重新分配引用来引用另一个对象。 They refer to the same object throughout their entire lifetime.它们在整个生命周期中都引用同一个对象。 When assignment operation is applied to a reference, then the referred object is assigned.当赋值操作应用于引用时,被引用的对象被赋值。

Note that Java has no explicit references.请注意,Java 没有显式引用。 All class type variables are references, and primitive variables are value-objects.所有类类型变量都是引用,原始变量是值对象。 C++ is different. C++ 是不同的。 You have to explicitly specify whether a variable is a reference or an object, and you can have value-objects of class types as well as references to fundamental types.您必须明确指定变量是引用还是对象,并且您可以拥有类类型的值对象以及对基本类型的引用。

In some ways Java references are more similar to C++ pointers than C++ references.在某些方面,Java 引用比 C++ 引用更类似于 C++ 指针。 In particular, pointers can be null, and can be assigned to point elsewhere, just like Java references.特别是,指针可以为空,并且可以分配到其他地方,就像 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



 

Looking at your statement:看你的说法:

I have been learning C++ for two weeks.我已经学习 C++ 两个星期了。

Congratulations a good step forward.恭喜你向前迈出了一大步。 :-) :-)

In Java, if we have two different objects of the same class, and if we assign the reference of one object to the other reference of the other object, then they refer to the same object.在Java中,如果我们有同一个类的两个不同对象,并且如果我们将一个对象的引用分配给另一个对象的另一个引用,那么它们引用的是同一个对象。

The difference here is in the definition of reference.这里的区别在于引用的定义。 What Java refer to as a reference C++ would refer to as a pointer (as they always refer to dynamically allocated objects that last beyond the current scope). Java 所指的引用 C++ 将引用为指针(因为它们总是引用持续超出当前范围的动态分配的对象)。 Though the Java reference is smart and the object is garbage collected when all "Java references" are gone.尽管 Java 引用是智能的,并且当所有“Java 引用”都消失时,对象会被垃圾收集。 So a Java reference would be more equivalent to a std::shared_ptr<> .因此,Java 引用将更等同于std::shared_ptr<>

// Java                       // C++
String s = new String("S");   std::shared_ptr<std::string> s = new std::string("S");

C++ automatic objects are more like Java "value types" (int/char/float). C++ 自动对象更像 Java“值类型”(int/char/float)。 The difference is that in C++ any type can act like a "value type".不同之处在于,在 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.

Afterward, changing a data member by one reference would change the data member in the other reference as well.之后,通过一个引用更改数据成员也会更改另一个引用中的数据成员。

Yes basically.基本上是的。 But you have to be careful of your wording.但是你必须小心你的措辞。 If the member is a reference then you are not changing this member (as references never change what they refer to in C++) you are changing the object that the reference is referring to.如果成员是引用,那么您不会更改此成员(因为引用永远不会更改它们在 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;

// Here a.x and b are still completely distinct objects.
// The content of b have been copied to a.x and 
// thus accessible from a.y

My question is: Isn't it the same in C++ too?我的问题是:在 C++ 中不也是一样吗?

No. Because references are not objects.不,因为引用不是对象。 You need to think of Java references being equivalent to a C++ std::shared_ptr.您需要将 Java 引用视为等同于 C++ std::shared_ptr。 This is how we manage dynamically allocated memory in C++.这就是我们在 C++ 中管理动态分配内存的方式。

I got a bit confused about Copy constructors and assignment operators.我对复制构造函数和赋值运算符有点困惑。 Both of them do deep copy.他们都做深拷贝。 Without them, we can only do shallow copy as far as I know.没有它们,据我所知,我们只能做浅拷贝。 I have a code snippet too.我也有一个代码片段。

Basically true.基本正确。 It basically allows you to define how assignment works correctly when copying one object into another object.它基本上允许您在将一个对象复制到另一个对象时定义分配如何正确工作。

Lets comment the code.让我们注释代码。
The output you describe is the output I would expect.您描述的输出是我期望的输出。

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

Test测试

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;
        }
};

Main主要的

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;
}

When you create the t2 instance, t2.ref is initialized to refer to t2.x :当您创建t2实例时, t2.ref被初始化为引用t2.x

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

Latter,后者,

t2 = t1;

does not change what t2.ref refers to (as it would be invalid: in C++ a reference cannot bind to a new referent):不会改变t2.ref所指的内容(因为它是无效的:在 C++ 中,引用不能绑定到新的引用对象):

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

t2.ref still refers to t2.x , whose value you never alter: t2.ref仍然指的是t2.x ,你永远不会改变它的值:

t1.setX(40);

In Java, you have two kinds of objects: those always passed by value, which are exclusively the native data types: int, long, double, ... and the non-native ones, implicitly inheriting from Object , which are stored in variables as references and passed on as such on assignment (eg to other variables or function parameters).在 Java 中,您有两种类型的对象:那些始终按值传递的对象,它们完全是本机数据类型:int、long、double 等;非本机对象,隐式继承自Object ,它们存储在变量中作为引用并在赋值时传递(例如,传递给其他变量或函数参数)。

In C++, any data type can show both types of behaviour, being passed by value or passed by reference.在 C++ 中,任何数据类型都可以显示两种类型的行为,通过值传递或通过引用传递。 And in C++, you have to be explicit about.在 C++ 中,您必须明确说明。

In C++, you have three ways, not just two, how you can pass on objects:在 C++ 中,您可以通过三种方式(而不仅仅是两种)传递对象:

  • by value按价值
  • by reference引用
  • by pointer通过指针

Java references are far closer to C++ pointers than to C++ references, though: You can re-assign pointers to different objects, you cannot do so for references. Java 引用比 C++ 引用更接近 C++ 指针,但是:您可以将指针重新分配给不同的对象,但不能为引用这样做。 It helps considering C++ references just as an alias (another name) for a concrete object, ignoring that under the hoods this might really apply in some cases (then no memory is allocated for at all) and in others (especially function parameters) not.它有助于将 C++ 引用视为具体对象的别名(另一个名称),而忽略了在某些情况下这可能真的适用(然后根本没有分配内存)而在其他情况下(尤其是函数参数)则不适用。

Now to be precise: Even in Java every variable is copied, even the reference variables.现在准确地说:即使在 Java 中,每个变量都会被复制,甚至引用变量也是如此。 But for the latter, it is only the reference itself that is copied, not the object!但是对于后者,复制的只是引用本身,而不是对象! So to show up the parallels:所以要显示相似之处:

// 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
}                               }

Should be known so far... To be honest, above is simplified too much : In Java, we have automatic garbage collection, in C++, we don't.到目前为止应该知道...说实话,上面的简化太多了:在Java中,我们有自动垃圾收集,在C++中,我们没有。 So actually, the string objects created above in C++ are never cleaned up any more and we have a memory leak!所以实际上,上面在 C++ 中创建的字符串对象再也没有被清理过,我们有内存泄漏! Modern C++ introduced smart pointers to facilitate the issue, consider the garbage collection in Java as well, the C++ code might rather look like:现代 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");
}

where std::shared_ptr does all the reference counting that in Java is hidden in the Object base class...其中std::shared_ptr执行 Java 中隐藏在Object基类中的所有引用计数...

Unlike in Java, you can pass the string by value, too, though:与 Java 不同,您也可以按值传递字符串:

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

Now the string will be copied into the local variable, just like the int.现在字符串将被复制到局部变量中,就像 int 一样。 Solely: How???唯独:怎么???

That's where the copy constructor comes into play.这就是复制构造函数发挥作用的地方。 If you want so, it is a recipe for how to create that copy, ie how to copy the internal representation into the new object.如果您愿意,这是如何创建该副本的秘诀,即如何将内部表示复制到新对象中。 For a std::string , this would be some internal, dynamically allocated array holding the string's contents, its current capacity (ie total length of the array) and its size (how much of the array actually is occupied).对于std::string ,这将是一些内部的、动态分配的数组,其中包含字符串的内容、其当前容量(即数组的总长度)及其大小(实际占用了多少数组)。

Finally the C++ reference:最后是 C++ 参考:

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

In this case, s will always refer to the object that was assigned to the function, you cannot change the reference (at least not legally, in some cases you could with some really dirty hacks...), only the referred object.在这种情况下, s 将始终引用分配给函数的对象,您不能更改引用(至少不合法,在某些情况下您可以使用一些非常肮脏的黑客...),只能更改引用的对象。

Actually, in C++ the issue is more complex:实际上,在 C++ 中,问题更复杂:

In Java, you get automatic memory management, there's a garbage collector selecting all objects that aren't referred to any more.在 Java 中,您可以获得自动内存管理,有一个垃圾收集器选择所有不再引用的对象。 Such thing doesn't exist in C++, we need to care for cleanup manually. C++ 中不存在这样的东西,我们需要手动进行清理。 Actually, the strings I created in the sample above aren't cleaned up at all, so we got a memory leak!!!实际上,我在上面的示例中创建的字符串根本没有清理,所以我们遇到了内存泄漏!!!

A more modern way is更现代的方式是

In his book on C++ Stroustrup has explained this thus: Stroustrup 在他关于 C++ 的书中解释了这一点:

Note that the two objects are independent.请注意,这两个对象是独立的。 We can change the value of y without affecting the value of x.我们可以改变 y 的值而不影响 x 的值。 For example x=99 will not change the value of y.例如 x=99 不会改变 y 的值。 Unlike Java, C#, and other languages, but like C, that is true for all types, not just for ints.与 Java、C# 和其他语言不同,但与 C 一样,这适用于所有类型,而不仅仅是整数。 If we want different objects to refer to the same (shared) value, we must say so.如果我们希望不同的对象引用相同的(共享的)值,我们必须这样说。 We could use pointers:我们可以使用指针:

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

架构

I arbitrarily chose 88 and 92 as the addresses of the ints.我随意选择了88和92作为int的地址。 Again, we can see that the assigned-to object gets the value from the assigned object, yielding two independent objects (here, pointers), with the same value.同样,我们可以看到被赋值对象从被赋值对象中获取值,产生两个具有相同值的独立对象(这里是指针)。 That is, p=q gives p==q.也就是说,p=q 给出 p==q。 After p=q, both pointers point to y.在 p=q 之后,两个指针都指向 y。 A reference and a pointer both refer/point to an object and both are represented in memory as a machine address.引用和指针都引用/指向对象,并且都在内存中表示为机器地址。 However, the language rules for using them differ.但是,使用它们的语言规则不同。 Assignment to a reference does not change what the reference refers to but assigns to the referenced object:对引用的赋值不会改变引用所指的内容,而是分配给被引用的对象:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM