【问题标题】:Is it a good practice to pass struct object as parameter to a function in c++?将 struct 对象作为参数传递给 c++ 中的函数是一种好习惯吗?
【发布时间】:2010-04-27 20:12:06
【问题描述】:

我在下面尝试了一个示例:

typedef struct point
{
    int x;
    int y;
} point;

void cp(point p)
{
    cout<<p.x<<endl;
    cout<<p.y<<endl;
}

int main()
{
    point p1;
    p1.x=1;
    p1.y=2;
    cp(p1);
}

打印出来的结果是:

1
2

这是我所期望的。我的问题是:参数 p 是否获得对象 p1 的完整副本?如果是这样,我想知道这是否是一个好习惯? (我假设当结构体变大时,会产生大量的复制开销)。

【问题讨论】:

  • 关于按值传递和按引用传递之间的差异和权衡还有许多其他问题。例如。 stackoverflow.com/questions/2627166/…
  • 在 C++ 中,您不需要执行 typedef struct point {...} point;。只需struct point {...};

标签: c++ function struct overhead


【解决方案1】:

是的,这没有任何问题,是的,pp1 的完整副本。我不会用两个大的 int 调用 struct,但是如果 struct 确实变大并且不需要在函数体中更改它,则可以考虑通过 const 引用传递它:

void cp(const point& p)
{
    // ...
}

【讨论】:

    【解决方案2】:

    将结构体作为参数传递并没有错。在 C++ 中,所有内容都是按值传递的,因此确实制作了完整的副本。

    您在示例中给出的结构很小,因此如果按值传递它可能不是问题。但如果您使用更大的数据结构,您可能希望通过引用传递它。

    但请注意,通过引用传递意味着如果您修改函数内部的结构,您的原始结构将被修改。确保在不修改结构的每种情况下都使用 const 关键字。这将为您提供有关您的函数是否修改信息的即时信息。

    您的示例可以修改为以这种方式使用引用:

    typedef struct point
    {
        int x;
        int y;
    } point;
    
    void cp(const point& p) // You can know that cp doesn't modify your struct
    {
        cout<<p.x<<endl;
        cout<<p.y<<endl;
    }
    
    void mod_point(point& p) // You can know that mod_points modifies your struct
    {
        p.x += 1;
        p.y += 1;
    }
    
    int main()
    {
        point p1;
        p1.x=1;
        p1.y=2;
        cp(p1);
        mod_point(p1);
        cp(p1); // will output 2 and 3
    }
    

    【讨论】:

    • 在 C++ 中并非所有内容都是按值传递的,但在 C 中就是这种情况(没有引用)。
    • 在函数声明中,除非您指定,否则所有内容都是按值传递的。这与大多数通过引用自动传递对象的现代语言非常不同。不过,您可以在不知道函数的情况下通过引用来传递参数,因为语义是相同的。
    • 谁能告诉我,为什么上面的代码不能被C编译器编译?
    【解决方案3】:

    在我回答您的问题之前(您可以在本文末尾找到它),这里简要总结了您将参数传递给函数的可能性:


    1.复制构造的对象(按值传递):

    void cp(point p);
    

    这是按值传递。一个临时的point 对象是从您传递给cp 的任何point 对象创建和复制构造的。一旦执行离开cp 函数,临时对象就会被销毁。

    请注意,由于该函数对传递给该函数的任何内容的副本进行操作,您可以根据需要修改该本地 point 对象,调用者的原始对象不会受到影响。无法使用按值传递的参数来改变这种行为。


    2。对对象的引用(按引用传递):

    void cp(point& p);
    

    这是按引用传递。传递给函数的实际对象在函数内部是可用的(并且可能会受到修改)。如果不希望函数能够更改传入的对象,请将参数声明为const point&amp;

    void cp(const point& p);
    

    3.指向对象的指针(通过引用):

    void cp(point* p);
    

    这也是通过引用传递,有一些细微的差别。一个显着的区别是您可以将空指针传递给函数。这对于引用是不可能的,因为引用必须被初始化并且之后不能被重新定位。 -- 与引用一样,如果您想禁止cp 更改传入的point,请将其声明为 const:

    void cp(const point* p);
    

    回答你的问题:

    按值传递参数在本质上并不是坏事。当然,有些类型的复制构造成本很高(例如非常大或复杂的对象),或者它具有某些副作用(例如std::auto_ptr)。在这些情况下,您应该小心。否则,它只是 C++ 的另一个特性,有其完全合理的用途。

    【讨论】:

      【解决方案4】:
      void cp(point p){
      }
      

      按值获取

      void cp(point *p){
      }
      

      通过引用得到它

      就像任何其他数据变量一样。

      在 java 中情况是不同的。传递对象总是通过引用。

      【讨论】:

      • point * 是指针,而不是引用。由于这是一个 C++ 问题,因此存在显着差异。
      • Java 实际上是按值传递,尽管这是另一个问题 ;)
      【解决方案5】:

      在您的情况下,点结构是“按值”传递的,意思是复制整个结构。对于大数据类型,这确实会很慢。

      可以考虑通过引用传递对象

      void cp(point& p) // p may be altered!
      

      或者是常量引用

      void cp(const point& p) // p may not be altered!
      

      【讨论】:

        【解决方案6】:

        查看避免按值传递对象的可能性。该对象不仅是“复制”的,而且是复制构造的。因此,如果您的对象由更多对象组成或派生,则这涉及调用复制构造函数和复制构造函数链。如果可能,通过引用传递。

        void cp(point &p const) const {

            cout << p.x << endl;
            cout << p.y << endl;
        

        }

        【讨论】:

        • 您在这里使用的 const 是错误的。正如其他人所说,它应该是void cp(const point &amp;p)
        • 请注意,如果您不打算修改参数,则该参数应为const point &amp;p。将const 放在声明部分之后和{ 之前对于不更改对象的成员函数来说是正确的。
        • 我不建议总是通过引用传递。有一个权衡,它并不总是明确支持引用。引用可能引用全局对象这一事实意味着无法在函数体中进行优化,而这些优化可能会使用复制的参数来执行。复制可能非常便宜(在这种情况下没有复制构造函数,并且根据调用约定,“复制”可能只是加载到寄存器中。
        • 对不起,伙计们。我同意 cmets 的观点。引用的压力是针对可能导致多个构造的组合对象。在这种情况下,是的,我同意按值传递可能会优化得更好。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-14
        • 2019-11-20
        • 2011-11-07
        • 1970-01-01
        • 1970-01-01
        • 2019-03-11
        相关资源
        最近更新 更多