如何将对象传递给 C++ 中的函数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2139224/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 22:14:07  来源:igfitidea点击:

How to pass objects to functions in C++?

c++pointerspass-by-referencepass-by-valuec++-faq

提问by Rakesh K

I am new to C++ programming, but I have experience in Java. I need guidance on how to pass objects to functions in C++.

我是 C++ 编程的新手,但我有 Java 的经验。我需要有关如何在 C++ 中将对象传递给函数的指导。

Do I need to pass pointers, references, or non-pointer and non-reference values? I remember in Java there are no such issues since we pass just the variable that holds reference to the objects.

我是否需要传递指针、引用或非指针和非引用值?我记得在 Java 中没有这样的问题,因为我们只传递保存对象引用的变量。

It would be great if you could also explain where to use each of those options.

如果您还可以解释在何处使用这些选项,那就太好了。

回答by sbi

Rules of thumb for C++11:

C++11的经验法则

Pass by value, except when

按值传递,除非

  1. you do not need ownership of the object and a simple alias will do, in which case you pass by constreference,
  2. you must mutate the object, in which case, use pass by a non-constlvalue reference,
  3. you pass objects of derived classes as base classes, in which case you need to pass by reference. (Use the previous rules to determine whether to pass by constreference or not.)
  1. 你不需要对象的所有权,一个简单的别名就可以了,在这种情况下你通过const引用传递
  2. 您必须改变对象,在这种情况下,使用传递非const左值引用
  3. 您将派生类的对象作为基类传递,在这种情况下,您需要通过引用传递。(使用前面的规则来决定是否通过const引用传递。)

Passing by pointer is virtually never advised. Optional parameters are best expressed as a std::optional(boost::optionalfor older std libs), and aliasing is done fine by reference.

实际上从不建议通过指针传递。可选参数最好表示为std::optional(boost::optional对于较旧的 std 库),并且通过引用可以很好地完成别名。

C++11's move semantics make passing and returning by value much more attractive even for complex objects.

即使对于复杂的对象,C++11 的移动语义也使按值传递和返回更具吸引力。



Rules of thumb for C++03:

C++03的经验法则

Pass arguments by constreference, except when

通过const引用传递参数,除非

  1. they are to be changed inside the function and such changes should be reflected outside, in which case you pass by non-constreference
  2. the function should be callable without any argument, in which case you pass by pointer, so that users can pass NULL/0/nullptrinstead; apply the previous rule to determine whether you should pass by a pointer to a constargument
  3. they are of built-in types, which can be passed by copy
  4. they are to be changed inside the function and such changes should notbe reflected outside, in which case you can pass by copy(an alternative would be to pass according to the previous rules and make a copy inside of the function)
  1. 它们将在函数内部进行更改,并且此类更改应反映在外部,在这种情况下,您将通过非const引用传递
  2. 该函数应该是可调用不带任何参数,在这种情况下,你通过指针传递,这样用户就可以通过NULL/ 0/nullptr代替; 应用前面的规则来确定是否应该传递指向const参数的指针
  3. 它们是内置类型,可以通过复制传递
  4. 它们的功能内被改变并且这些变化应被反射的外部,在这种情况下,可以通过复制通过(一个替代方法是,根据前述规则,以通过和使函数的副本内)

(here, "pass by value" is called "pass by copy", because passing by value always creates a copy in C++03)

(这里,“传值”被称为“传值”,因为传值总是在C++03中创建一个副本)



There's more to this, but these few beginner's rules will get you quite far.

这还有更多内容,但这些初学者规则会让您走得更远。

回答by David Rodríguez - dribeas

There are some differences in calling conventions in C++ and Java. In C++ there are technically speaking only two conventions: pass-by-value and pass-by-reference, with some literature including a third pass-by-pointer convention (that is actually pass-by-value of a pointer type). On top of that, you can add const-ness to the type of the argument, enhancing the semantics.

C++ 和 Java 中的调用约定存在一些差异。在 C++ 中,从技术上讲只有两种约定:值传递和引用传递,一些文献包括第三个指针传递约定(实际上是指针类型的值传递)。最重要的是,您可以向参数类型添加常量,从而增强语义。

Pass by reference

通过引用传递

Passing by reference means that the function will conceptually receive your object instance and not a copy of it. The reference is conceptually an alias to the object that was used in the calling context, and cannot be null. All operations performed inside the function apply to the object outside the function. This convention is not available in Java or C.

通过引用传递意味着该函数将在概念上接收您的对象实例,而不是它的副本。该引用在概念上是调用上下文中使用的对象的别名,并且不能为空。在函数内部执行的所有操作都适用于函数外部的对象。此约定在 Java 或 C 中不可用。

Pass by value (and pass-by-pointer)

按值传递(和按指针传递)

The compiler will generate a copy of the object in the calling context and use that copy inside the function. All operations performed inside the function are done to the copy, not the external element. This is the convention for primitive types in Java.

编译器将在调用上下文中生成对象的副本,并在函数内部使用该副本。在函数内部执行的所有操作都是对副本完成的,而不是外部元素。这是 Java 中原始类型的约定。

An special version of it is passing a pointer (address-of the object) into a function. The function receives the pointer, and any and all operations applied to the pointer itself are applied to the copy (pointer), on the other hand, operations applied to the dereferenced pointer will apply to the object instance at that memory location, so the function can have side effects. The effect of using pass-by-value of a pointer to the object will allow the internal function to modify external values, as with pass-by-reference and will also allow for optional values (pass a null pointer).

它的一个特殊版本是将指针(对象的地址)传递给函数。函数接收指针,对指针本身的任何和所有操作都应用于副本(指针),另一方面,应用于解引用指针的操作将应用于该内存位置的对象实例,因此函数可能有副作用。使用指向对象的指针的值传递的效果将允许内部函数修改外部值,就像传递引用一样,并且还允许可选值(传递空指针)。

This is the convention used in C when a function needs to modify an external variable, and the convention used in Java with reference types: the reference is copied, but the referred object is the same: changes to the reference/pointer are not visible outside the function, but changes to the pointed memory are.

这是函数需要修改外部变量时在 C 中使用的约定,Java 中使用引用类型的约定:引用被复制,但被引用的对象是相同的:对引用/指针的更改在外部不可见功能,但对指向内存的更改是。

Adding const to the equation

将 const 添加到等式中

In C++ you can assign constant-ness to objects when defining variables, pointers and references at different levels. You can declare a variable to be constant, you can declare a reference to a constant instance, and you can define all pointers to constant objects, constant pointers to mutable objects and constant pointers to constant elements. Conversely in Java you can only define one level of constant-ness (final keyword): that of the variable (instance for primitive types, reference for reference types), but you cannot define a reference to an immutable element (unless the class itself is immutable).

在 C++ 中,您可以在定义不同级别的变量、指针和引用时为对象分配常量。您可以将变量声明为常量,可以声明对常量实例的引用,还可以定义所有指向常量对象的指针、指向可变对象的常量指针和指向常量元素的常量指针。相反,在 Java 中,您只能定义一个级别的常量(final 关键字):变量级别(基本类型的实例,引用类型的引用),但您不能定义对不可变元素的引用(除非类本身是不可变)。

This is extensively used in C++ calling conventions. When the objects are small you can pass the object by value. The compiler will generate a copy, but that copy is not an expensive operation. For any other type, if the function will not change the object, you can pass a reference to a constant instance (usually called constant reference) of the type. This will not copy the object, but pass it into the function. But at the same time the compiler will guarantee that the object is not changed inside the function.

这在 C++ 调用约定中广泛使用。当对象很小时,您可以按值传递对象。编译器将生成一个副本,但该副本不是一项昂贵的操作。对于任何其他类型,如果函数不会更改对象,则可以传递对该类型的常量实例(通常称为常量引用)的引用。这不会复制对象,而是将其传递给函数。但同时编译器会保证该对象在函数内部不被改变。

Rules of thumb

经验法则

This are some basic rules to follow:

这是要遵循的一些基本规则:

  • Prefer pass-by-value for primitive types
  • Prefer pass-by-reference with references to constant for other types
  • If the function needs to modify the argument use pass-by-reference
  • If the argument is optional, use pass-by-pointer (to constant if the optional value should not be modified)
  • 首选按值传递原始类型
  • 更喜欢通过引用传递引用其他类型的常量
  • 如果函数需要修改参数,请使用 pass-by-reference
  • 如果参数是可选的,则使用传递指针(如果不应该修改可选值,则为常量)

There are other small deviations from these rules, the first of which is handling ownership of an object. When an object is dynamically allocated with new, it must be deallocated with delete (or the [] versions thereof). The object or function that is responsible for the destruction of the object is considered the owner of the resource. When a dynamically allocated object is created in a piece of code, but the ownership is transfered to a different element it is usually done with pass-by-pointer semantics, or if possible with smart pointers.

这些规则还有其他小的偏差,第一个是处理对象的所有权。当一个对象用 new 动态分配时,它必须用 delete(或其 [] 版本)解除分配。负责销毁对象的对象或函数被认为是资源的所有者。当在一段代码中创建动态分配的对象,但所有权转移到不同的元素时,通常使用传递指针语义完成,或者如果可能,使用智能指针。

Side note

边注

It is important to insist in the importance of the difference between C++ and Java references. In C++ references are conceptually the instance of the object, not an accessor to it. The simplest example is implementing a swap function:

坚持 C++ 和 Java 引用之间差异的重要性是很重要的。在 C++ 中,引用在概念上是对象的实例,而不是它的访问器。最简单的例子是实现一个交换函数:

// C++
class Type; // defined somewhere before, with the appropriate operations
void swap( Type & a, Type & b ) {
   Type tmp = a;
   a = b;
   b = tmp;
}
int main() {
   Type a, b;
   Type old_a = a, old_b = b;
   swap( a, b );
   assert( a == old_b );
   assert( b == old_a ); 
}

The swap function above changesboth its arguments through the use of references. The closest code in Java:

上面的 swap 函数通过使用引用来改变它的两个参数。Java中最接近的代码:

public class C {
   // ...
   public static void swap( C a, C b ) {
      C tmp = a;
      a = b;
      b = tmp;
   }
   public static void main( String args[] ) {
      C a = new C();
      C b = new C();
      C old_a = a;
      C old_b = b;
      swap( a, b ); 
      // a and b remain unchanged a==old_a, and b==old_b
   }
}

The Java version of the code will modify the copies of the references internally, but will not modify the actual objects externally. Java references are C pointers without pointer arithmetic that get passed by value into functions.

Java 版本的代码将在内部修改引用的副本,但不会在外部修改实际对象。Java 引用是没有指针运算的 C 指针,它们按值传递给函数。

回答by David Rodríguez - dribeas

There are several cases to consider.

有几种情况需要考虑。

Parameter modified ("out" and "in/out" parameters)

参数修改(“out”和“in/out”参数)

void modifies(T &param);
// vs
void modifies(T *param);

This case is mostly about style: do you want the code to look like call(obj)or call(&obj)? However, there are two points where the difference matters: the optional case, below, and you want to use a reference when overloading operators.

这种情况主要与样式有关:您希望代码看起来像call(obj)还是call(&obj)?但是,有两点区别很重要:下面的可选情况,以及您想在重载运算符时使用引用。

...and optional

...和可选

void modifies(T *param=0);  // default value optional, too
// vs
void modifies();
void modifies(T &param);

Parameter not modified

参数未修改

void uses(T const &param);
// vs
void uses(T param);

This is the interesting case. The rule of thumb is "cheap to copy" types are passed by value — these are generally small types (but not always) — while others are passed by const ref. However, if you need to make a copy within your function regardless, you should pass by value. (Yes, this exposes a bit of implementation detail. C'est le C++.)

这是一个有趣的案例。经验法则是“廉价复制”类型按值传递——这些通常是小类型(但不总是)——而其他类型则通过 const ref 传递。但是,如果您无论如何都需要在函数中进行复制,则应该通过 value 传递。(是的,这暴露了一些实现细节。C'est le C++。

...and optional

...和可选

void uses(T const *param=0);  // default value optional, too
// vs
void uses();
void uses(T const &param);  // or optional(T param)

There's the least difference here between all situations, so choose whichever makes your life easiest.

所有情况之间的差异最小,因此请选择使您的生活最轻松的方式。

Const by value is an implementation detail

Const by value 是一个实现细节

void f(T);
void f(T const);

These declarations are actually the exact same function!When passing by value, const is purely an implementation detail. Try it out:

这些声明实际上是完全相同的功能!当按值传递时,const 纯粹是一个实现细节。 试试看:

void f(int);
void f(int const) { /* implements above function, not an overload */ }

typedef void NC(int);       // typedefing function types
typedef void C(int const);

NC *nc = &f;  // nc is a function pointer
C *c = nc;    // C and NC are identical types

回答by nav

Pass by value:

按值传递:

void func (vector v)

Pass variables by value when the function needs complete isolation from the environment i.e. to prevent the function from modifying the original variable as well as to prevent other threads from modifying its value while the function is being executed.

当函数需要与环境完全隔离时,按值传递变量,即防止函数修改原始变量以及防止其他线程在执行函数时修改其值。

The downside is the CPU cycles and extra memory spent to copy the object.

缺点是 CPU 周期和用于复制对象的额外内存。

Pass by const reference:

通过常量引用:

void func (const vector& v);

This form emulates pass-by-value behavior while removing the copying overhead. The function gets read access to the original object, but cannot modify its value.

这种形式在消除复制开销的同时模拟传值行为。该函数可以读取原始对象,但不能修改其值。

The downside is thread safety: any change made to the original object by another thread will show up inside the function while it's still executing.

缺点是线程安全:另一个线程对原始对象所做的任何更改都将在函数仍在执行时显示在函数内部。

Pass by non-const reference:

通过非常量引用:

void func (vector& v)

Use this when the function has to write back some value to the variable, which will ultimately get used by the caller.

当函数必须将一些值写回变量时使用它,这最终将被调用者使用。

Just like the const reference case, this is not thread-safe.

就像 const 参考案例一样,这不是线程安全的。

Pass by const pointer:

通过 const 指针传递:

void func (const vector* vp);

Functionally same as pass by const-reference except for the different syntax, plus the fact that the calling function can pass NULL pointer to indicate it has no valid data to pass.

除了语法不同之外,在功能上与通过常量引用传递相同,加上调用函数可以传递 NULL 指针以指示它没有要传递的有效数据的事实。

Not thread-safe.

不是线程安全的。

Pass by non-const pointer:

通过非常量指针传递:

void func (vector* vp);

Similar to non-const reference. The caller typically sets the variable to NULL when the function is not supposed to write back a value. This convention is seen in many glibc APIs. Example:

类似于非常量引用。当函数不应该写回值时,调用者通常将变量设置为 NULL。在许多 glibc API 中都可以看到这种约定。例子:

void func (string* str, /* ... */) {
    if (str != NULL) {
        *str = some_value; // assign to *str only if it's non-null
    }
}

Just like all pass by reference/pointer, not thread-safe.

就像所有通过引用/指针传递一样,不是线程安全的。

回答by murali krish

Since no one mentioned I am adding on it, When you pass a object to a function in c++ the default copy constructor of the object is called if you dont have one which creates a clone of the object and then pass it to the method, so when you change the object values that will reflect on the copy of the object instead of the original object, that is the problem in c++, So if you make all the class attributes to be pointers, then the copy constructors will copy the addresses of the pointer attributes , so when the method invocations on the object which manipulates the values stored in pointer attributes addresses, the changes also reflect in the original object which is passed as a parameter, so this can behave same a Java but dont forget that all your class attributes must be pointers, also you should change the values of pointers, will be much clear with code explanation.

由于没有人提到我正在添加它,当您将对象传递给 c++ 中的函数时,如果您没有创建对象的克隆然后将其传递给方法的对象的默认复制构造函数,则会调用该对象的默认复制构造函数,因此当您更改将反映在对象副本而不是原始对象上的对象值时,这就是 C++ 中的问题,因此,如果您将所有类属性都设置为指针,那么复制构造函数将复制对象的地址指针属性,所以当方法调用对象的方法操作存储在指针属性地址中的值时,变化也会反映在作为参数传递的原始对象中,所以这可以表现得与 Java 相同,但不要忘记你的所有类属性必须是指针,你也应该改变指针的值,代码解释会很清楚。

Class CPlusPlusJavaFunctionality {
    public:
       CPlusPlusJavaFunctionality(){
         attribute = new int;
         *attribute = value;
       }

       void setValue(int value){
           *attribute = value;
       }

       void getValue(){
          return *attribute;
       }

       ~ CPlusPlusJavaFuncitonality(){
          delete(attribute);
       }

    private:
       int *attribute;
}

void changeObjectAttribute(CPlusPlusJavaFunctionality obj, int value){
   int* prt = obj.attribute;
   *ptr = value;
}

int main(){

   CPlusPlusJavaFunctionality obj;

   obj.setValue(10);

   cout<< obj.getValue();  //output: 10

   changeObjectAttribute(obj, 15);

   cout<< obj.getValue();  //output: 15
}

But this is not good idea as you will be ending up writing lot of code involving with pointers, which are prone for memory leaks and do not forget to call destructors. And to avoid this c++ have copy constructors where you will create new memory when the objects containing pointers are passed to function arguments which will stop manipulating other objects data, Java does pass by value and value is reference, so it do not require copy constructors.

但这不是一个好主意,因为您最终将编写大量涉及指针的代码,这些代码容易发生内存泄漏并且不要忘记调用析构函数。为了避免这种 C++ 有复制构造函数,当包含指针的对象被传递给函数参数时,你将创建新的内存,这将停止操作其他对象数据,Java 确实按值传递,值是引用,因此它不需要复制构造函数。

回答by Yogeesh H T

The following are the ways to pass a arguments/parameters to function in C++.

以下是在 C++ 中将参数/参数传递给函数的方法。

1. by value.

1. 按价值。

// passing parameters by value . . .

void foo(int x) 
{
    x = 6;  
}

2. by reference.

2. 参考。

// passing parameters by reference . . .

void foo(const int &x) // x is a const reference
{
    x = 6;  
}

// passing parameters by const reference . . .

void foo(const int &x) // x is a const reference
{
    x = 6;  // compile error: a const reference cannot have its value changed!
}

3. by object.

3.按对象。

class abc
{
    display()
    {
        cout<<"Class abc";
    }
}


// pass object by value
void show(abc S)
{
    cout<<S.display();
}

// pass object by reference
void show(abc& S)
{
    cout<<S.display();
}

回答by amerr

There are three methods of passing an object to a function as a parameter:

将对象作为参数传递给函数的方法有以下三种:

  1. Pass by reference
  2. pass by value
  3. adding constant in parameter
  1. 通过引用传递
  2. 按值传递
  3. 在参数中添加常量

Go through the following example:

通过以下示例:

class Sample
{
public:
    int *ptr;
    int mVar;

    Sample(int i)
    {
        mVar = 4;
        ptr = new int(i);
    }

    ~Sample()
    {
        delete ptr;
    }

    void PrintVal()
    {
        cout << "The value of the pointer is " << *ptr << endl
             << "The value of the variable is " << mVar;
   }
};

void SomeFunc(Sample x)
{
cout << "Say i am in someFunc " << endl;
}


int main()
{

  Sample s1= 10;
  SomeFunc(s1);
  s1.PrintVal();
  char ch;
  cin >> ch;
}

Output:

输出:

Say i am in someFunc
The value of the pointer is -17891602
The value of the variable is 4

说我在 someFunc
指针的值为 -17891602
变量的值为 4