"传值"就是通过值来传递一个对象,这个过程需要拷贝构造函数来进行。而"传引用"实质上就是一种指针传递。两种传递方式在使用上存在效率问题和"切割"问题。
1、效率
而前所述,"传值"需要调用拷贝构造函数。例如:
class CTest
{
public:
CTest()
{
cout << "CTest::CTest()" << endl;
}
CTest(const CTest& ref)
{
cout << "CTest::CTest(const CTest& ref)" << endl;
}
~CTest()
{
cout << "CTest::~CTest()" << endl;
}
CTest fun(CTest obj)
{
return obj;
}
};
void main()
{
CTest Obj, obj1;
CTest obj2 = obj.fun(obj1);
}
上述代码的输出结果是:
CTest::CTest()
CTest::CTest()
CTest::CTest(const CTest& ref)
CTest::CTest(const CTest& ref)
CTest::~CTest()
CTest::~CTest()
CTest::~CTest()
CTest::~CTest()
在调用fun()成员函数时,传入实参调用一次拷贝构造函数,返回值调用一次拷贝构造函数。也就是说:调用一次fun()成员函数,需要调用两次拷贝构造函数和两次析构函数。如此昂贵的开销不得不让人有所警惕。
定义一个对象的引用就是给对象起个别名,无需调用拷贝构造函数。因而,将fun()成员函数的形参和返回值改为"传引用",能避免调用拷贝构造函数和析构函数。这社会,能节省开销就要节省开销。
*注:在上述例子中,将返回值改为"传引用"只是为了说明问题,其实返回局部对象的引用,其后果是难以预测的。
2、切割问题
当一个派生类的对象作为基类对象被传递时,它(派生类对象)的作为派生类所具有的行为特性会被“切割”掉,从而变成了一个简单的基类对象。例如:
class CBase
{
public:
virtual void Display()
{
cout << "CBase::Display()" << endl;
}
};
class CDerive : public CBase
{
public:
virtual void Display()
{
cout << "CDerive::Display()" << endl;
}
};
void fun(CBase ObjCBase)
{
ObjCBase.Diaplay();
}
这时,如果执行下面语句:
CDerive ObjCDerive;
fun(ObjCDerive);
很抱歉,这时执行的是CBase::Display()。这就是切割问题。其实也不难理解:fun()申请的是CBase对象的空间BUF1,这时跑来的却是CDerive对象的空间BUF2,BUF1可装不下BUF2,不好意思,BUF2中除BUF1以外的空间必须切割掉。这种切割问题很明显违反了程序员的意图。
如果将fun()的形参改为"传引用",问题就回到多态性上了。"传引用"实质就是一种地址传递,这时候基类的指针指向派生类,因而调用的虚函数就是派生类的虚函数。这时程序员的意图得到了表达。