传递常量是通过引用自动传递的吗?

Posted

技术标签:

【中文标题】传递常量是通过引用自动传递的吗?【英文标题】:Passing a constant is automatically passed by reference? 【发布时间】:2014-10-23 23:28:54 【问题描述】:

如果我将变量传递给函数但将该变量标记为常量,它会自动通过引用传递吗?


在 C++ 中

void foo(const int a)

测试后:否,因为它打印不同的内存位置。

void foo(const int a)

    cout << &a << endl;


int main()

   int a = 5;
   foo(a);
   cout << &a << endl;
   return 0;


C

不对吧?因为我只能传递一个指向内存位置的指针,而不是真正通过引用。喜欢这里:Passing by reference in C


Java

也许编译器可以计算出final 应该指向同一个内存位置?

void foo(final int a)

C#

和 Java 一样吗?

void foo(const int a)


我知道我的问题中包含了 4 种语言,但由于我经常使用这 4 种语言,所以我很想知道。

有没有办法强制 Java 和 C# 处理这个问题?我知道在 C++ 中我可以将 &amp; 添加到函数中,这正是我想要的。

 void f(const int &a)

这就是我希望 Java 和 C# 编译器在幕后所做的事情。

【问题讨论】:

所有这些在语义上都是按值传递的。编译器可以在底层进行优化吗?当然,这是可能的。 我猜你问这个是因为你经常看到 C++ 函数原型将 const 对象的引用作为参数。这是一种有意的优化,允许函数参数值成为右值,而不必为函数调用复制它们(并且 C++11 添加了右值引用,作为替代方案)。但是,默认情况下不会通过引用传递 const 函数参数。 +1 表示具有多个语言标签的问题,这些标签实际上都与它们相关。 Java 原语没有参考。 Java 总是按值传递。 您的问题“有没有办法强制 Java 和 C# 处理 [] this”的目的是什么?如果您想传递相同的内存位置,以便该方法可以修改参数并允许调用者看到修改后的值,那么在 Java 中有一些方法可以做到这一点(我不太了解 C#)。但如果不是,那你为什么希望编译器在幕后传递地址,你为什么要关心?请注意,通过地址传递 int 往往会使程序变慢,因为它需要间接访问该值。 【参考方案1】:

在Java中,所有的变量都是按值传递的。

基元实际上是复制的,而对象变量无论如何都是引用,这意味着对象变量的值实际上是引用 -> 您正在传递引用的值。

尽管如此,您总是按值传递,就像在 C++ 中一样,如果您不显式使用引用,您将按值传递。

我几乎可以肯定在 C# 中也是如此。

编辑:我忘了 C

在 C 中也一样:默认按值传递。显然,您可以传递一个指针。在 C++ 中,这是扩展的,因此您可以将引用作为参数:

void function(int& intByReference) 
    cout<<&intByReference<<endl; //prints x


int main() 
    int n = 0;
    cout<<&n<<endl; //prints x
    function(n);

由于 C++ 包含类,所以有一个默认构造函数来复制对象:

class MyClass 

    public:
        MyClass(MyClass& objectToCopy) 
            //copy your object here
            //this will be called when you pass an instance of MyClass to a function
        
;

【讨论】:

剩下的部分呢? C++ 部分也是 :-) 你实际上是按值传递的,如果你直接传递对象,它们甚至会被构造函数复制。考虑到这一点,只有在 C# 中它是相同的才有意义 - 按值传递应该是默认的,尤其是对于原语,这是唯一有意义的事情。 在 C# 中也是如此。它总是按值传递,除非参数标记为refout,在这种情况下它是按引用传递。【参考方案2】:

您错误地认为您的直觉可以指导您了解语言的细节是如何实现的。这根本不是真的。对于 C++ 和 C,规范非常清楚地说明了应该如何处理。

Java 作为按值传递的语言可能会在最终/常量/未触及值的规范中对此行为做出一些规定。 (我不知道)但只有规范可以允许这种行为。

【讨论】:

这并不完全清楚。您首先暗示这些是实现细节,然后继续提及与实现细节无关的规范...... @OliCharlesworth 不,我是说他将直觉外推到规范所涵盖的内容上。如果我不清楚,请编辑或建议编辑。【参考方案3】:

在回答你的问题之前,让我举例说明一个例子来测试你的场景。

假设您通过传递变量int a 从方法void calledFrom() 调用方法void called(int a )

据我了解:

如果任何方法中 a 值的变化影响另一个(calledFrom 中 a 的变化会影响 called 中 a 的值)然后它通过引用调用否则它调用按价值

现在让我们在 java 中使用 final 进行测试

public class MyClass

    int a;
    public void setA(final int a)
    
        this.a=a;
    
    public void printA()
    
        System.out.println(a);
    


从 main 方法你可以这样做:

int a=10;
MyClass mclass=new MyClass();

mclass.setA(a);
mclass.printA();
a++;
mclass.printA();

输出是: 10 10

所以在main 中进行更改不会更改MyClassa 的值。所以对public void setA(final int a) 的调用不是按引用调用,而是按值调用。

密钥 final 可能会引起一些混乱。当您在方法签名中编写 final 时,不会以任何其他方式产生影响 except you cant change the value of that parameter inside that method. 例如,如果你写

public void setA(final int a)

    a++; //error
    this.a=a;

你会得到一个编译错误

但是你可以很容易地做到这一点

 public void printA()

    a++; //ok
    System.out.println(a);

我认为这不仅在 java 中所有其他语言都有相同的行为。你可以试试其他语言。

要获得关于在 java 上下文中按值传递和按引用传递的清晰概念,您可以阅读 this 线程

或阅读this答案

【讨论】:

以上是关于传递常量是通过引用自动传递的吗?的主要内容,如果未能解决你的问题,请参考以下文章

C++ 通过常量引用传递和通过常量引用返回

通过值或常量引用传递给函数?

便宜的值传递与常量引用传递

地图是通过价值或Go中的参考传递的吗?

如何通过值或常量引用传递 std::string_view

传递常量引用与创建临时常量副本相同,那么为啥要传递常量引用呢? [复制]