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

本文深入探讨了C++中对象的传递方式,包括值传递、引用传递及其对构造和析构函数的影响,揭示了对象传递背后的机制。

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

第三方支付功能的技术人员;尤其适合从事电商、在线教育、SaaS类项目开发的工程师。; 使用场景及目标:① 实现微信与支付宝的Native、网页/APP等主流支付方式接入;② 掌握支付过程中关键的安全机制如签名验签、证书管理与敏感信息保护;③ 构建完整的支付闭环,包括下单、支付、异步通知、订单状态更新、退款与对账功能;④ 通过定任务处理内容支付超与概要状态不一致问题:本文详细讲解了Java,提升系统健壮性。; 阅读应用接入支付宝建议:建议结合官方文档与沙微信支付的全流程,涵盖支付产品介绍、开发环境搭建箱环境边学边练,重点关注、安全机制、配置管理、签名核心API调用及验签逻辑、异步通知的幂等处理实际代码实现。重点与异常边界情况;包括商户号与AppID获取、API注意生产环境中的密密钥与证书配置钥安全与接口调用频率控制、使用官方SDK进行支付。下单、异步通知处理、订单查询、退款、账单下载等功能,并深入解析签名与验签、加密解密、内网穿透等关键技术环节,帮助开发者构建安全可靠的支付系统。; 适合人群:具备一定Java开发基础,熟悉Spring框架HTTP协议,有1-3年工作经验的后端研发人员或希望快速掌握第三方支付集成的开发者。; 使用场景及目标:① 实现微信支付Native模式与支付宝PC网页支付的接入;② 掌握支付过程中核心的安全机制如签名验签、证书管理、敏感数据加密;③ 处理支付结果异步通知、订单状态核对、定任务补偿、退款及对账等生产级功能; 阅读建议:建议结合文档中的代码示例与官方API文档同步实践,重点关注支付流程的状态一致性控制、幂等性处理异常边界情况,建议在沙箱环境中完成全流程测试后再上线。
matlab2python 这是一个Python脚本,用于将Matlab文件或代码行转换为Python。此项目处于alpha阶段。 该实现严重依赖于Victor Leikehman的SMOP项目。 当前实现围绕SMOP构建,具有以下差异: 力图生成不依赖libsmop的代码,仅使用如numpy这样的标准Python模块。 使用常见缩写如np而非全称numpy。 尝试重排数组循环索引,从0开始而不是1。 不使用来自libsmop的外部类matlabarraycellarray。 增加了对Matlab类的基本支持,类体中声明的属性在构造函数中初始化。 因上述改动,产出代码“安全性较低”,但可能更接近用户自然编写的代码。 实现相对直接,主要通过替换SMOP使用的后端脚本,这里称为smop\backend_m2py.py。 一些函数替换直接在那里添加。 额外的类支持、导入模块以及其他微调替换(或说是黑客技巧)在matlabparser\parser.py文件中完成。 安装 代码用Python编写,可按如下方式获取: git clone https://github.com/ebranlard/matlab2python cd matlab2python # 安装依赖项 python -m pip install --user -r requirements.txt # 让包在任何目录下都可用 python -m pip install -e . # 可选:运行测试 pytest # 可选:立即尝试 python matlab2python.py tests/files/fSpectrum.m -o fSpectrum.py 使用方法 主脚本 存储库根目录下的主脚本可执行,并带有一些命令行标志(部分直接取自SMOP)。要将文件file.m转换为file.py,只需输入:
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值