如何在c++中传递对象给函数?

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

我需要传递指针,引用,或非指针和非引用值吗?我记得在Java中不存在这样的问题,因为我们只传递包含对象引用的变量。

如果你能解释一下在哪里使用这些选项,那就太好了。

143208 次浏览

c++ 11:的经验法则

传递< >强价值< / >强,除非

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

实际上不建议传递指针。可选参数最好表示为std::optional(旧std库为boost::optional),并且通过引用可以很好地实现混叠。

c++ 11的move语义使得按值传递和返回更有吸引力,即使是对复杂的对象。


c++ 03:的经验法则

传递参数by const reference,除非when

  1. 它们将在函数内部被更改,并且这些更改应该反映在函数外部,在这种情况下,您传递非-const引用 . 传递非-const引用
  2. 函数应该是可调用的,没有任何参数,在这种情况下传递指针,以便用户可以传递NULL/0/nullptr;应用前面的规则来确定你是否应该传递指向const实参的指针
  3. 它们是内置类型,可以是传递给copy
  4. 它们将在函数内部被更改,这些更改应该<强> < / >强不反映在函数外部,在这种情况下,你可以pass by copy(另一种选择是根据前面的规则传递,并在函数内部复制)

(这里,“pass by value”;被称为“传递复制”,因为在c++ 03中传递值总是创建一个副本)。


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

c++和Java在调用约定方面有一些不同。在c++中,从技术上讲只有两种约定:值传递和引用传递,一些文献还包括第三种指针传递约定(实际上是指针类型的值传递)。在此基础上,您可以向实参类型添加const-ness,从而增强语义。

通过引用传递

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

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

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

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

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

在方程中加入const

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

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

经验法则

以下是一些需要遵循的基本规则:

  • 对基元类型更倾向于值传递
  • 对于其他类型,建议使用对常量的引用的引用传递
  • 如果函数需要修改实参,则使用引用传递
  • 如果参数是可选的,则使用pass-by-pointer(如果不应修改可选值,则为常量)

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

边注

强调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指针,它通过值传递给函数。

有几种情况需要考虑。

参数修改("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);

这是一个有趣的例子。经验法则是“易于复制”的类型是通过值传递的-这些通常是小类型(但不总是)-而其他类型是通过const ref传递的。然而,如果你需要在函数中进行复制,则应该按值传递. conf将被调用。(是的,这暴露了一些实现细节。这是c++。)

...和可选的

void uses(T const *param=0);  // default value optional, too
// vs
void uses();
void uses(T const &param);  // 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

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

  1. 通过引用传递
  2. 按值传递
  3. 在参数中添加常数

看下面的例子:

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

输出:

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

按值传递:

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

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

因为没人提到我添加它,当你用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
}

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

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

我需要传递指针,引用,或非指针和非引用值吗?

这是一个在编写函数和选择参数类型时很重要的问题。这个选择将影响函数的调用方式,这取决于一些事情。

最简单的选择是按值传递对象。这基本上是在函数中创建对象的副本,这有很多优点。但有时复制是昂贵的,在这种情况下,常量引用const&通常是最好的。有时你需要你的对象被函数改变。然后需要一个非常量引用&

有关参数类型选择的指导,请参见c++核心指南的函数部分,从F.15开始。作为一般规则,尽量避免使用原始指针*