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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值