您的位置:首页 > 移动开发 > Objective-C

CRITICAL SKILL9.3:PassingObjects to Functions传递对象参数给函数(值传递和引用传递的探讨)

2011-10-18 15:34 585 查看
CRITICAL SKILL9.3:PassingObjects to Functions传递对象参数给函数

An object can be passed to afunction in the same way as any other data type. Objects are passed tofunctions using the normal C++ call-by-value parameter-passing convention. Thismeans that a copy of the object, not the actual
object itself, is passed to thefunction. Therefore, changes made to the object inside the function do notaffect the object used as the argument to the function. The following programillustrates this point:

#include <iostream>
using namespace std; 
class MyClass
{
         intval;
public:
         MyClass(inti)
         {
                   val = i;
         }
         intgetval(){return val;}
         voidsetval(int i){val = i;}
}; 
void display(MyClass ob)
{
         cout<<ob.getval()<<"\n";
}
void change(MyClass ob)
{
         ob.setval(100); //no effect on argumetn
         cout<<"Valueof ob inside the change(): ";
         display(ob);
}
int main()
{
         MyClass a(10);
         cout<<"beforecalling change(), a is :";
         display(a);
         change(a);
         cout<<"after calling change(), a is :";
         display(a);
         return0;
}
Output:



As the output shows, changing thevalue of ob inside change( ) has no effect on a inside main( ).

>>>>>>对象(和其他数据类型一样)可以传递给函数。使用c++常用的值传递惯例来传递对象。这意味这值传递实际上是对对象的一个拷贝,并不是实际的对象本身传递给了函数。因此,在函数里面对对象的改变不影响传递给函数的实参对象。以下的函数说明了这一点:

<<<<<<码片和输出如上所示>>>>>>
如上所示,在change( )函数内部对ob对象的修改不会影响在main( )函数里面的对象。

 

Constructors, Destructors, and Passing Objects 构造函数、析构函数和对象传递

Although passing simple objects asarguments to functions is a straightforward procedure, some rather unexpectedevents occur that relate to constructors and destructors. To understand why,consider this short program:

#include <iostream>
using namespace std; 
class MyClass
{
         intval;
public:
         MyClass(inti)
         {
                   val = i;
                   cout<<"Inside constructor val is : "<<val<<"\n";
         }
         ~MyClass( ){ cout<<"Destructing val is : "<<val<<"\n";}
         intgetval(){return val;}
         voidsetval(int i){val = i;}
};
void display(MyClass ob)
{      
         cout<<"\n    -------->> in diaplay( ) function,the val copy from outside object is ";
         cout<<ob.getval();
         cout<<"\n    -------->> in display( ) function,set the copied object's val to 5\n";
         ob.setval(5);    
}
int main()
{
         MyClass a(10);
         cout<<"  before calling display()";
         display(a);
         cout<<"  after calling display()\n";
         return0;
}
Output:



As you can see, there is one callto the constructor (which occurs when a is created), but there are two calls tothe destructor. Let’s see why this is the case.

When an object is passed to afunction, a copy of that object is made. (And this copy becomes the parameterin the function.) This means that a new object comes into existence. When thefunction terminates, the copy of the argument
(that is, the parameter) isdestroyed. This raises two fundamental questions: First, is the object’sconstructor called when the copy is made? Second, is the object’s destructorcalled when the copy is destroyed? The answers may, at first, surprise you.

When a copy of an argument is madeduring a function call, the normal constructor is not called. Instead, theobject’s copy constructor is called. A copy constructor defines how a copy ofan object is made. (Later in this module
you will see how to create a copyconstructor.)

However, if a class does notexplicitly define a copy constructor, then C++ provides one by default. Thedefault copy constructor creates a bitwise (that is, identical) copy of theobject.

The reason a bitwise copy is madeis easy to understand if you think about it. Since a normal constructor is usedto initialize some aspect of an object, it must not be called to make a copy ofan already existing object. Such
a call would alter the contents of the object.When passing an object to a function, you want to use the current state of theobject, not its initial state.

However, when the functionterminates and the copy of the object used as an argument is destroyed, thedestructor function is called. This is necessary because the object has goneout of scope. This is why the preceding program
had two calls to thedestructor. The first was when the parameter to display( ) went out of scope.The second is when a inside main( ) was destroyed when the program ended.

To summarize: When a copy of anobject is created to be used as an argument to a function, the normalconstructor is not called. Instead, the default copy constructor makes abit-by-bit identical copy. However, when the copy
is destroyed (usually byg
4000
oing out of scope when the function returns), the destructor is called.

>>>>>>尽管对象作为实参传递给函数是一种直接的方法,但是涉及到构造函数和析构函数就会一些意想不到的事情发生,为什么呢,思考下面的码片:

<<<<<<码片和输出如上所示>>>>>>
       可见,对构造函数的调用只有一次(就在a被创建的时候出现),但是对析构函数调用了两次。来看看是怎么发生的。当一个对象被传递给一个函数的时候,就会做一次对象的拷贝。(这个拷贝就成为了函数里面的形参)这就意味着有一个新的对象出现,当函数结束的时候,实参的拷贝(就是刚才拷贝的形参)就会销毁(实际上是涉及到了形参的作用域问题,不论形参是哪种类型,她的作用域只在函数体里面,当函数结束的时候,形参的作用域就结束了。在此,当形参ob在函数display(MyClass ob )里面的时候其作用才发挥作用,退出的时候作用域就要消亡,对类的对象来说消亡的时候就要调用析构函数;同理在对象a在主函数main(
)里面的是有作用域是有效的,当main函数结束的时候就会象ob那样调用a的析构函数)这样就产生两个基本疑问:1,当拷贝对象产生的时候,拷贝对象的构造函数调用了吗?2,当拷贝对象消亡的时候拷贝对象析构函数调用了吗?回答可能会有些出人意料。

       在函数调用的期间,就会发生一次实参的拷贝,正常的构造函数不会被调用,相反对象拷贝的构造函数会被调用。拷贝构造函数定义了一个对象的拷贝是如何发生实现的。(晚些时候会讲怎么创建一个拷贝构造函数)

       然而,如果一个类没有明确的定义一个拷贝构造函数,那么c++会默认的提供一个。默认的拷贝构造函数实现的是一个对象的bits位拷贝(拷贝的内容和父对象相同,注意在对象里面有指针的情况)。位拷贝的实现是易于理解的。由于正常的构造函数被用于初始化对象的某些方面,但对于已经存在的拷贝对象就不用调用构造函数(bits位拷贝的时候就已经初始化了对象)。如果调用了构造函数反而会更改了对象的内容(会被构造函数在此初始化,对象的值会回到初始状态,而和实参传递过来的值不一样)。当给函数传递对象的时候,使用的是对象的当前状态,而不是初始化的状态。

       尽管如此,当函数结束的时候,实参对象的一份拷贝就会被销毁,那么析构函数就会被调用。析构函数是必需调用的,因为拷贝的对象跑出了作用域范围。这就是之前代码为什么会出现两个析构函数调用的原因。第一个发生在离开display( )函数作用域的时候;第二个是发生在程序main( )函数结束(离开了main函数的作用域)的时候。 

 

Passing Objects by Reference 引用传递对象

Another way that you can pass anobject to a function is by reference. In this case, a reference to the objectis passed, and the function operates directly on the object used as anargument. Thus, changes made to the parameter
will affect the argument, andpassing an object by reference is not applicable to all situations. However, inthe cases in which it is, two benefits result. First, because only an addressto the object is being passed rather than the entire object, passing an
objectby reference can be much faster and more efficient than passing an object byvalue. Second, when an object is passed by reference, no new object comes intoexistence, so no time is wasted constructing or destructing a temporary object.

Here is an example that illustratespassing an object by reference:

#include <iostream>
using namespace std; 
class MyClass
{
         intval;
public:
         MyClass(inti)
         {
                   val = i;
                   cout<<"Inside constructor val is : "<<val<<"\n";
                   cout<<"Inside constructor, the address of this object is:"<<this<<"\n";
                   cout<<"Inside constructor, the address of this object'sval is :"<<&val<<"\n";
         }
         ~MyClass( ){ cout<<"Destructing val is : "<<val<<"\n";
                                     cout<<"Inside deconstructor, the address of this object is:"<<this<<"\n";
                                     cout<<"Inside deconstructor, the address of this object'sval is :"<<&val<<"\n";
         }
         intgetval(){return val;}
         voidsetval(int i){val = i;}
}; 
void display(MyClass &ob)
{      
         cout<<"\n    -------->> in diaplay( ) function,from preference &ob, the val is ";
         cout<<ob.getval();
         cout<<"\n    -------->> in display( ) function,from preference &ob, the val will be set to 5\n";
         ob.setval(5);    

int main()
{
         MyClass a(10);
         cout<<"  before calling display()";
         display(a);
         cout<<"  after calling display()\n";
         return0;
}
Output:



In this program, display() usereference parameters. Thus, the address of the argument, not a copy of theargument, is passed, and the functions operate directly on the argument. Forexample, when display( ) is called, a is passed
by reference. Thus, display() madeto the parameter ob in display() affect a in main( ). Also, notice that onlyone call to the constructor and one call to the destructor is made. This isbecause only one object, a, is created and destroyed. No temporary objects
areneeded by the program.

>>>>>>传递对象给函数另一个方法是引用传递。这种情况下,对象的引用就会被传递给函数,函数就像是在使用实参那样直接在对象上操作。因此对形参的更改会影响实参,但是传递对象的引用并不适合所有的情况。尽管如此,在使用引用传递的时候,有两大益处。1、因为仅仅传递的是对象的地址而不是整个对象,所以,引用传递比整个对象传递(之前所见到的值传递)更快更高效。2、引用传递不会有新的对象产生(不消耗很多的内存),所以不会再当前的对象上浪费构造和析构的时间。这里有一个例子用于说明对象引用的传递情况:

<<<<<<码片和输出如上所示>>>>>>
       在这个代码片里,display()函数使用引用的形参。因此,实参的地址(不是实参的内容拷贝)被传递,函数直接在实参上操作。如,当display()被调用的时候,a的引用被传递给函数形参。在display()里面对ob对象的修改会影响在main()函数里面的a对象。而且注意到,只调用了一次构造函数和析构函数。这是因为只有一个对象a被创建、被销毁。程序不需要临时对象。

A Potential Problem When Passing Objects    当传递对象的潜在问题

Even when objects are passed tofunctions by means of the normal call-by-value parameter-passing mechanism,which, in theory, protects and insulates the calling argument, it is stillpossible for a side effect to occur that may
affect, or even damage, the objectused as an argument. For example, if an object allocates some system resource(such as memory) when it is created and frees that resource when it isdestroyed, then its local copy inside the function will free that same resourcewhen
its destructor is called. This is a problem because the original object isstill using this resource. This situation usually results in the originalobject being damaged.

One solution to this problem is topass an object by reference, as shown in the preceding section. In this case,no copy of the object is made, and thus, no object is destroyed when thefunction returns. As explained, passing
objects by reference can also speed upfunction calls, because only the address of the object is being passed.However, passing an object by reference may not be applicable to all cases.Fortunately, a more general solution is available: you can create your ownversion
of the copy constructor. Doing so lets you define precisely how a copyof an object is made, allowing you to avoid the type of problems justdescribed. However, before examining the copy constructor, let’s look atanother, related situation that can also benefit
from a copy constructor.

>>>>>>即使对象使用的是值传递(形参传递机制),理论上是保护和隔离了实参,但是仍然有副作用甚至是破坏性的影响实参。如,如果一个对象在创建的时候分配了系统的资源(例如内存),在销毁的时候回释放资源,那么在函数里面的局部拷贝在对象析构函数(销毁)调用的时候会释放相同的资源。这会造成仍然在使用资源的原始对象被毁坏。

       解决办法是使用引用来传递对象,在之前描述可以了解到引用的使用。在这种情况下,没有产生对象的拷贝,因此在函数返回的时候没有对象被销毁。如前说描述,引用传递可以加速函数的调用,因为只有对象的地址被传递。然而,引用传递对象并不适合所有的情况。庆幸的是,更通用的解决方案是:创建我们自己的拷贝构造函数。自己精确的定义一个对象是如何拷贝的,以避免值传递和引用传递出现的意外情况。在检验拷贝构造函数之前,先看下critical skill 9.4:return objects,他也有从拷贝构造函数里吸取参考相关情况。

>>>>>>>>>>>>>>>>>>>>>>>translated by :Mr
ouyangjun

>>>>>>>>>>>>>>>>>>>>>>>e-mail:ouyangjun1985#msn.com
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息