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

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

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

如果你也可以解释在哪里使用这些选项,那将是非常好的。

C ++ 11的经验法则

按价值传递,除了什么时候

  1. 你不需要所有权的对象和一个简单的别名会做,在这种情况下,你通过const引用
  2. 你必须改变对象,在这种情况下,使用const左值引用传递
  3. 您将派生类的对象作为基类传递,在这种情况下,您需要通过引用传递 。 (使用以前的规则来确定是否通过const引用传递。)

通过指针几乎从来没有build议。 可选参数最好用boost::optional表示,并且通过引用可以很好boost::optional别名。

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


C ++的经验法则03:

通过const引用传递参数,除了when

  1. 它们在函数内部被改变,这样的改变应该被reflection到外面,在这种情况下,你通过非const参考
  2. 该函数应该是可调用的,没有任何参数,在这种情况下,您通过指针传递,以便用户可以传递NULL / 0 / nullptr ; 应用先前的规则来确定是否应该传递一个指向const参数的指针
  3. 它们是内置types,可以通过复制传递
  4. 它们在函数内部被改变,这样的改变不应该被reflection到外面,在这种情况下,你可以通过复制 (另一种方法是按照以前的规则进行传递并在函数内部复制)

(在这里,“通过值传递”被称为“通过复制”,因为按值传递总是在C ++中创build一个副本03)


还有更多,但这几个初学者的规则会让你相当远。

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

通过参考传递

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

按值传递(并传递指针)

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

它的一个特殊版本是将一个指针(对象的地址)传递给一个函数。 该函数接收指针,并且将应用于指针本身的所有操作应用于副本(指针),另一方面,应用于取消引用的指针的操作将应用于该存储器位置处的对象实例,所以函数可以有副作用。 使用指针传递给对象的效果将允许内部函数修改外部值,就像通过引用一样,也允许可选的值(传递一个空指针)。

这是C中当一个函数需要修改一个外部variables时使用的约定,以及Java中引用types使用的约定:引用被复制,但引用的对象是相同的:对引用/指针的更改不可见该function,但改变了针对性的记忆。

在等式中添加const

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

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

经验法则

这是一些基本的规则:

  • 为原始types优先传递值
  • 对于其他types,请使用引用常量的引用
  • 如果函数需要修改参数使用传递引用
  • 如果参数是可选的,则使用pass-by-pointer(如果不应该修改可选值,则为常量)

这些规则还有一些小的偏差,第一个是处理对象的所有权。 当一个对象被dynamic地分配给新对象时,它必须被删除(或者其中的[]版本)。 负责销毁对象的对象或函数被认为是资源的所有者。 当在一段代码中创build一个dynamic分配的对象时,所有权被转移到另一个元素上时,通常使用按指针传递语义,或者如果可能的话使用智能指针。

边注

坚持C ++和Java引用之间区别的重要性是非常重要的。 在C ++中,引用在概念上是对象的实例,而不是对象的访问器。 最简单的例子是实现交换function:

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

上面的交换函数通过使用引用来改变它的参数。 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 } } 

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

有几种情况需要考虑。

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

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

这种情况主要是关于样式:你想要代码看起来像调用(obj)调用(&obj) ? 但是,有两点差异很重要:下面是可选的情况,并且在重载操作符时要使用引用。

…和可选的

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

参数未修改

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

这是一个有趣的案例。 经验法则是“廉价的复制”types是通过值传递 – 这些通常是小types(但不是总是) – 而其他人通过const ref传递。 但是,如果你需要在你的函数中做一个副本,你应该通过价值传递 。 (是的,这暴露了一些实现细节。C'est le C ++。

…和可选的

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

这里所有情况之间的差别最小,所以select哪一个让你的生活变得最简单。

通过值的Const是一个实现细节

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

这些声明实际上是完全相同的function! 当通过值传递时,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 

按价值传递:

 void func (vector v) 

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

缺点是CPU周期和复制对象所花费的额外内存。

通过const引用传递:

 void func (const vector & v); 

此表单模拟传递值行为,同时消除复制开销。 该函数获取对原始对象的读取权限,但不能修改其值。

缺点是线程安全性:任何由另一个线程对原始对象所做的更改都会在该函数仍在执行时显示出来。

通过非const引用传递:

 void func (vector & v) 

当函数必须向variables写回一些值的时候使用这个函数,这个函数最终会被调用者使用。

就像const引用的情况一样,这不是线程安全的。

通过const指针传递:

 void func (const vector * vp); 

在function上和通过const-reference传递除了不同的语法外,加上调用函数可以传递NULL指针来表明它没有有效数据传递的事实。

不是线程安全的。

通过非const指针传递:

 void func (vector * vp); 

类似于非const引用。 当函数不应该回写一个值时,调用者通常将该variables设置为NULL。 这个约定在许多glibc API中被看到。 例:

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

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

由于没有人提到我加上它,当你传递一个对象到一个函数在C ++中,如果你没有创build对象的克隆然后传递给方法的对象的默认拷贝构造函数被调用,所以当你改变将反映对象的副本而不是原始对象的对象值时,这就是c ++中的问题,所以如果你把所有的类属性都变成指针,那么拷贝构造函数将拷贝指针的属性,所以当方法调用对象的操作存储在指针属性地址中的值时,这些更改也反映在作为parameter passing的原始对象中,所以这可以performance为一个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 } 

但是这不是一个好主意,因为你会写很多涉及指针的代码,这些指针很容易出现内存泄漏,不要忘记调用析构函数。 而为了避免这个c ++有复制构造函数,当你将创build新的内存时,包含指针的对象被传递给函数参数将停止操作其他对象的数据,Java传递的值和值是引用,所以它不需要复制构造函数。

有三种将对象作为parameter passing给函数的方法:

  1. 通过参考传递
  2. 按价值传递
  3. 在参数中join常量

通过下面的例子:

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

输出:

说我在一些function
指针的值是-17891602
variables的值是4

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

1.价值。

 // passing parameters by value . . . void foo(int x) { x = 6; } 

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.按对象。

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