为什么这个程序员特别通过引用在函数中传递指针?

Why does this programmer in particular passes pointers in functions by reference?

本文关键字:函数 指针 引用 程序员 为什么      更新时间:2023-10-16

我看到了这个特殊的代码:

void GetMe(User& user) {
     std::cout << user.getName() << std::endl;
}
main(...) {
     User* user = new User("Joe");
     GetMe(*user);
     getchar();
     return EXIT_SUCCESS;
}

我找不到一个例子,特别是在堆栈溢出的任何问题上,只有一些(&*)而不是(&),我不明白它背后的意义是什么,它是为了安全,以防指针在其他地方被删除?(这是一个多线程应用程序)

当您将用户定义的类型传递给函数时,您可以使用以下方法之一:

  1. 按值传递:

    void GetMe(User user) { ... }
    
  2. 通过指针传递:

    void GetMe(User* userPtr) { ... }
    void GetMe(User const* userPtr) { ... }
    
  3. 通过引用:

    void GetMe(User& user) { ... }
    void GetMe(User const& userPtr) { ... }
    

根据经验,按引用传递比按值传递或按指针传递更可取。

通过引用传递而不是通过值传递,可以避免复制对象的成本。

当你传递指针时,你不能假设nullptr不会被传递。所以,你要在GetMe中添加检查来解释这种情况。当你通过引用传递时,你就不用担心了。

我相信你是在问为什么代码不写成:

void GetMe(User* user) {
     std::cout << user->getName() << std::endl;
}
main(...) {
     User* user = new User("Joe");
     GetMe(user);
     getchar();
     return EXIT_SUCCESS;
}

但是很多有经验的程序员反而会问为什么它不是。

void GetMe(User const& user) {
     std::cout << user.getName() << std::endl;
}
main(...) {
     User* user = new User("Joe");
     GetMe(*user);
     getchar();
     return EXIT_SUCCESS;
}

在这两个例子中,我忽略了更严重的内存泄漏非智能指针问题,而只关注如何将user传递给GetMe。

我认为你问的这个问题很大程度上是一个风格问题。我没有看到多线程或其他功能上的差异(正如你所问的),也没有强烈的理由支持这两种风格。

但我确实看到了标记它const时,无论是通过指针或引用传递强有力的论据。如果getName()不是const(导致更难使用户const),那么可能也应该修复。

在我看来,你的问题有两个部分:

  1. 为什么是void GetMe(User& user) { }而不是void GetMe(User* user) {} ?可能有以下几个原因:

    void GetMe(User& user) { }意味着一个可伸缩的非空输入输出 user。如果您想要不可延展性或只能输入,请使用void GetMe(User const& user) { }。如果你想要空的,使用指针等…

在c++中传递参数的方式有非常具体的语义,所以你必须非常小心。它们都有不同的含义:通过引用传递,通过指针传递,通过const引用传递,通过const指针传递,等等……

  • 为什么用User* user = new User("Joe");代替User user("Joe"); ?您希望在堆上动态分配内存,而不是在堆栈上本地分配内存,这可能是有原因的。例如,如果您希望对象的生存期比右括号长。另一个原因是,如果User是一个非常大的对象,那么您希望它在堆上(对于现代cpu,这可能不是一个问题)。
  • 注意多线程是一个完全不同的问题。你必须担心内存可见性、同步、互斥、竞争条件等... ...这些问题大多与上述两点是正交的。如果你不想浪费大量的时间来调试线程问题,我建议你在走上这条路之前掌握好基本的c++语义。

    默认情况下,参数按值传递给函数,即不带&,你在函数中有一个参数的副本——只是它的值的副本,而不是它的引用——也就是说,你在函数中没有真正的参数,如果你必须对参数的地址(引用)做一些操作,一切都会出错。

    考虑这个例子:一个函数不能分配内存给它的形参(char):

    void al(char * c) //wrong way: pass by value
    {
    c =new char[10];
    }
    main()
    {
    char * x;
    al(x);//don't allocated because pass by value/.
     *x='a';//memory error//
     }
    

    但是这个函数声明可以这样做,因为通过引用传递:

     void al(char * & c)