我是c++编程的新手,但我有Java的经验。我需要关于如何在c++中传递对象到函数的指导。
我需要传递指针,引用,或非指针和非引用值吗?我记得在Java中不存在这样的问题,因为我们只传递包含对象引用的变量。
如果你能解释一下在哪里使用这些选项,那就太好了。
我是c++编程的新手,但我有Java的经验。我需要关于如何在c++中传递对象到函数的指导。
我需要传递指针,引用,或非指针和非引用值吗?我记得在Java中不存在这样的问题,因为我们只传递包含对象引用的变量。
如果你能解释一下在哪里使用这些选项,那就太好了。
当前回答
有几种情况需要考虑。
参数修改("out"和"in/out"参数)
void modifies(T ¶m);
// vs
void modifies(T *param);
这个例子主要是关于样式:你想让代码看起来像call(obj)还是call(&obj)?但是,有两点是有区别的:下面的可选情况,以及在重载操作符时希望使用引用。
...和可选的
void modifies(T *param=0); // default value optional, too
// vs
void modifies();
void modifies(T ¶m);
参数未修改
void uses(T const ¶m);
// vs
void uses(T param);
这是一个有趣的例子。经验法则是“容易复制”的类型是通过值传递的-这些通常是小类型(但不总是)-而其他类型是通过const ref传递的。然而,如果你需要在你的函数中进行复制,你应该通过值传递。(是的,这暴露了一些实现细节。这是c++。)
...和可选的
void uses(T const *param=0); // default value optional, too
// vs
void uses();
void uses(T const ¶m); // or optional(T param)
所有情况之间的差异都很小,所以选择任何让你的生活更轻松的情况。
Const by value是一个实现细节
void f(T);
void f(T const);
这些声明实际上是完全相同的函数!当按值传递时,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
其他回答
c++ 11的经验法则:
通过值传递,除非
你不需要对象的所有权,一个简单的别名就可以了,在这种情况下,你传递const引用, 你必须改变对象,在这种情况下,使用一个非const左值引用, 将派生类的对象作为基类传递,在这种情况下需要通过引用传递。(使用前面的规则来确定是否通过const引用传递。)
实际上不建议传递指针。可选参数最好表示为std:: Optional(对于旧的std库boost:: Optional),并且通过引用可以很好地实现混叠。
c++ 11的move语义使得按值传递和返回更有吸引力,即使是对复杂的对象。
c++ 03的经验法则:
通过const引用传递参数,除非
they are to be changed inside the function and such changes should be reflected outside, in which case you pass by non-const reference the function should be callable without any argument, in which case you pass by pointer, so that users can pass NULL/0/nullptr instead; apply the previous rule to determine whether you should pass by a pointer to a const argument they are of built-in types, which can be passed by copy they are to be changed inside the function and such changes should not be 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)
(这里,“传递值”被称为“传递复制”,因为在c++ 03中传递值总是创建一个副本)
还有更多,但这几个初学者的规则会让你走得很远。
我需要传递指针,引用,或非指针和非引用值吗?
这是一个在编写函数和选择参数类型时很重要的问题。这个选择将影响函数的调用方式,这取决于一些事情。
最简单的选择是按值传递对象。这基本上是在函数中创建对象的副本,这有很多优点。但有时复制的代价很高,在这种情况下,常量引用const&s通常是最好的。有时你需要你的对象被函数改变。然后需要一个非常量引用&。
有关参数类型选择的指导,请参阅c++核心指南的函数部分,从F.15开始。作为一般规则,尽量避免使用原始指针,*。
按值传递:
void func (vector v)
当函数需要与环境完全隔离时,按值传递变量,即防止函数修改原始变量,以及防止其他线程在函数执行时修改其值。
缺点是复制对象要花费CPU周期和额外的内存。
通过const引用传递:
void func (const vector& v);
此表单模拟值传递行为,同时消除复制开销。该函数获得对原始对象的读访问权,但不能修改其值。
缺点是线程安全:其他线程对原始对象所做的任何更改都将在函数仍在执行时显示出来。
通过非const引用传递:
void func (vector& v)
当函数必须回写一些值给变量时,使用此方法,这些值最终将被调用者使用。
就像const引用情况一样,这不是线程安全的。
通过const指针传递:
void func (const vector* vp);
与通过const-reference传递的功能相同,只是语法不同,加上调用函数可以传递NULL指针,以表明它没有要传递的有效数据。
不是线程安全的。
传递非const指针:
void func (vector* vp);
类似于非const引用。当函数不应该回写值时,调用者通常将变量设置为NULL。在许多glibc api中都可以看到这个约定。例子:
void func (string* str, /* ... */) {
if (str != NULL) {
*str = some_value; // assign to *str only if it's non-null
}
}
就像所有通过引用/指针传递一样,不是线程安全的。
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.
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++有复制构造函数,当包含指针的对象被传递给函数参数时,你将创建新的内存,这将停止操作其他对象的数据,Java是通过值和值是引用传递的,所以它不需要复制构造函数。
c++和Java在调用约定方面有一些不同。在c++中,从技术上讲只有两种约定:值传递和引用传递,一些文献还包括第三种指针传递约定(实际上是指针类型的值传递)。在此基础上,您可以向实参类型添加const-ness,从而增强语义。
通过引用传递
通过引用传递意味着函数将在概念上接收您的对象实例,而不是它的副本。引用在概念上是调用上下文中使用的对象的别名,不能为空。在函数内部执行的所有操作都适用于函数外部的对象。这个约定在Java或C中是不可用的。
按值传递(和按指针传递)
编译器将在调用上下文中生成对象的副本,并在函数中使用该副本。在函数内部执行的所有操作都是对副本执行的,而不是外部元素。这是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).
这是C语言中当函数需要修改外部变量时使用的约定,也是Java引用类型中使用的约定:引用被复制,但引用对象是相同的:对引用/指针的更改在函数外部是不可见的,但对指向内存的更改是可见的。
在方程中加入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).
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.
经验法则
以下是一些需要遵循的基本规则:
对基元类型更倾向于值传递 对于其他类型,建议使用对常量的引用的引用传递 如果函数需要修改实参,则使用引用传递 如果参数是可选的,则使用pass-by-pointer(如果不应修改可选值,则为常量)
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.
边注
强调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 );
}
上面的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
}
}
Java版本的代码将在内部修改引用的副本,但不会在外部修改实际的对象。Java引用是没有指针算术的C指针,它通过值传递给函数。