Passing by Reference for Efficiency
传统的传值方法,参数传入时,执行一次copy,函数返回时,又一次copy执行,这样很消耗内存,运行效率慢。C++引入了copy constructor 可以解决这个问题。
下面的代码演示上面的这句话:定义一个SimpleCat类,创建两个函数,分别通过pass by value 和 pass by reference OR pointer, 返回指针。 传reference不需要复制对象,缺点是把原对象直接暴露在被调函数中。
#include <QCoreApplication> #include <iostream> class SimpleCat { public: SimpleCat(); SimpleCat(SimpleCat&); ~SimpleCat(); }; SimpleCat::SimpleCat() { std::cout << "SimpleCat Constructor Performed ..." << std::endl; } SimpleCat::SimpleCat(SimpleCat&) { std::cout << "SimpleCat Copy Constructor Performed ..." << std::endl; } SimpleCat::~SimpleCat() { std::cout << "SimpleCat Destructor Performed ..." << std::endl; } SimpleCat functionOne(SimpleCat srcCat); SimpleCat *functionTwo(SimpleCat *srcCat); int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); std::cout << "Making a cat ..." << std::endl; SimpleCat Frisky; std::cout << "\n\n\n\n\n"; std::cout << "\n\n\n\n\n"; std::cout << "Calling functionOne ..." << std::endl; functionOne(Frisky); std::cout << "\n\n\n\n\n"; std::cout << "\n\n\n\n\n"; std::cout << "Calling functionTwo ..." << std::endl; functionTwo(&Frisky); return a.exec(); } //FunctionOne pass by value SimpleCat functionOne(SimpleCat srcCat) { std::cout << "Function one. Returning ..." << std::endl; return srcCat; } //FunctionTwo pass by pointer SimpleCat *functionTwo(SimpleCat *srcCat) { std::cout << "Function two. Returning ..." << std::endl; return srcCat; }
执行结果如下图:
函数1:
- main函数传值给函数1,相当于复制一个object给被调函数,所以复制构造函数被执行->SimpleCat Copy Constructor Performed ... 这里srcCat的存储位置在stack
- 函数1执行-> function one returning
- 函数1返回object给主函数(传值复制方式),再一次调用复制构造函数,把处理后的object复制给主函数。
- 第三步里子函数向主函数复制object时,产生临时对象(tempObject 我们看不见),在返回结束后被销毁,所以执行了一次constructor和destructor
- 函数1返回后,该函数结束,srcCat 生命周期结束,调用destructor
函数2:
- 主函数向子函数传递参数使用reference,未调用constructor
- 子函数向主函数返回的也是reference(返回值srcCat实际上是Frisky的地址)
传值方式,调用两次复制构造函数和两次析构函数,然后传reference方式一次也不调用
Pass a const Pointer
为什么?
上面的函数2,效率挺高,但是存在风险。直接把地址给了子函数,子函数可以为所欲为,通常主函数不想改变原始值。传值方式提供了这种保护,不过太蠢了,效率太慢了。
既想要传值方式的安全,又想要传引用方式的效率——传const pointer
#include <QCoreApplication> #include <iostream> class SimpleCat { public: SimpleCat(); SimpleCat(SimpleCat&); ~SimpleCat(); int getAge() const {return itsAge;} void setAge(int age) {itsAge = age;} private: int itsAge; }; SimpleCat::SimpleCat() { std::cout << "SimpleCat Constructor Performed ..." << std::endl; itsAge = 1; } SimpleCat::SimpleCat(SimpleCat&) { std::cout << "SimpleCat Copy Constructor Performed ..." << std::endl; } SimpleCat::~SimpleCat() { std::cout << "SimpleCat Destructor Performed ..." << std::endl; } const SimpleCat & functionTwo(const SimpleCat & srcCat); int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); std::cout << "Making a cat ..." << std::endl; SimpleCat Frisky; std::cout << "Frisky is " << Frisky.getAge() << "years old" << std::endl; int age = 5; Frisky.setAge(age); std::cout << "Frisky is " << Frisky.getAge() << "years old" << std::endl; std::cout << "Calling FunctionTwo ..." << std::endl; functionTwo(Frisky); std::cout << "Frisky is " << Frisky.getAge() << "years old" << std::endl; return 0; return a.exec(); } //函数进来指向常对象的引用,函数返回指向常对象的引用 //FUC2 takes and returns a reference to a constant object const SimpleCat & functionTwo(const SimpleCat &srcCat) { std::cout << "Function Two. Returning..." << std::endl; std::cout << "Frisky is now" << srcCat.getAge() << "years old" << std::endl; //srcCat.setAge(8); return srcCat; }
注意高亮部分,主次被注释掉的语句,如果takes a reference to a constant object,就不可以修改了,这样就达到了安全的目的,至于函数2最前面的那个const我就不知道啥意思了
什么时候用引用,什么时候用指针?
引用用起来方便,能用就尽量用
引用只能被赋值一次,所有如果要多次赋值,就用指针。引用不可以为NULL,所有如果有不确定对象,用指针
不可以返回对局部变量的引用
int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Man &rMan = theFunction(); std::cout << "rMan is " << rMan.getAge() << " years old!" << std::endl; return 0; return a.exec(); } Man &theFunction() { Man bob(5,9); return bob; }
编译也许会通过,但是一定会系统崩溃。
返回对heap上对象的引用
Man &theFunction(); int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Man &rMan = theFunction(); std::cout << &rMan << std::endl; std::cout << "rMan is " << rMan.getAge() << " years old!" << std::endl; return 0; return a.exec(); } Man &theFunction() { Man *pBob = new Man(5,9); std::cout << pBob << std::endl; return *pBob; }
执行结果,可以看到,即使在theFunction域外,堆上的这个对象存活了下来,函数返回后还可以使用。
这节课太难了……