1. 如何利用按引用传递来提高程序的效率
1.1 按引用传递以提高效率
-
请看下面这段代码,体会精神
class SimpleCat { public: SimpleCat(); //构造函数 SimpleCat(SimpleCat&); //复制构造函数 ~SimpleCat(); //析构函数 }; SimpleCat::SimpleCat() { cout << "调用构造函数SimpleCat::SimpleCat()"<<endl; } SimpleCat::SimpleCat(SimpleCat&) { cout << "调用复制构造函数SimpleCat::SimpleCat(SimpleCat&)" << endl; } SimpleCat::~SimpleCat() { cout << "调用析构函数SimpleCat::~SimpleCat()" << endl; } SimpleCat Func1(SimpleCat theCat); SimpleCat* Func2(SimpleCat* theCat); int main() { cout << "0. 初始化一个SimpleCat对象Frisky" << endl; SimpleCat Frisky; cout << endl << "1. 调用Func1函数(传值)" << endl; Func1(Frisky); cout << endl << "2. 调用Func2函数(传引用)" << endl; Func2(&Frisky); return 0; } SimpleCat Func1(SimpleCat theCat) { cout << "调用Func1函数(传值)SimpleCat Func1(SimpleCat theCat)" << endl; return theCat; } SimpleCat* Func2(SimpleCat* theCat) { std::cout << "调用Func2函数(传引用)SimpleCat* Func2(SimpleCat* theCat)" << endl; return theCat; } //0. 初始化一个SimpleCat对象Frisky //调用构造函数SimpleCat::SimpleCat() //1. 调用Func1函数(传值) //调用复制构造函数SimpleCat::SimpleCat(SimpleCat&) //调用Func1函数(传值)SimpleCat Func1(SimpleCat theCat) //调用复制构造函数SimpleCat::SimpleCat(SimpleCat&) //调用析构函数SimpleCat::~SimpleCat() //调用析构函数SimpleCat::~SimpleCat() //2. 调用Func2函数(传引用) //调用Func2函数(传引用)SimpleCat* Func2(SimpleCat* theCat) //调用析构函数SimpleCat::~SimpleCat()
0. 初始化一个SimpleCat对象Frisky 调用构造函数SimpleCat::SimpleCat() //初始化了一个 SimpleCat 对象 Frisky,导致构造函数被调用 1. 调用Func1函数(传值) 调用复制构造函数SimpleCat::SimpleCat(SimpleCat&) //调用 Func1 函数时,将 SimpleCat 对象按值传递给了它,因此将在栈中创建该 SimpleCat 对象的备份,作为调用函数的本地对象,这导致了复制构造函数被调用 调用Func1函数(传值)SimpleCat Func1(SimpleCat theCat) //调用 Func1 函数自身的内容 调用复制构造函数SimpleCat::SimpleCat(SimpleCat&) //Func1 函数返回时,按值返回 SimpleCat 对象,这将创建另一个对象备份,致使复制构造函数被调用 调用析构函数SimpleCat::~SimpleCat() //Func1 函数返回的对象没有赋给任何变量,因此该对象被丢弃,调用析构函数 调用析构函数SimpleCat::~SimpleCat() //Func1 函数结束,其本地备份不再在作用域内,调用析构函数 2. 调用Func2函数(传引用) 调用Func2函数(传引用)SimpleCat* Func2(SimpleCat* theCat) //参数按引用传递,不会创建备份,直接调用 Func2 函数自身的内容 //然后按引用返回 SimpleCat 对象,也不调用构造函数和析构函数 调用析构函数SimpleCat::~SimpleCat() //全部程序结束后,Frisky 不再在作用域内,最后一次调用析构函数
-
看不太懂代码不重要,看输出结果体会精神,总之就是
按引用传递可提高效率
1.2 传递 const 指针
-
直接将指针传递给函数比较危险,容易一不小心就把对象的值给修改了
-
传递一个 const指针,可以同时获得按值传递的安全性和按引用传递的效率
class SimpleCat { public: SimpleCat(); SimpleCat(SimpleCat&); ~SimpleCat(); }; SimpleCat::SimpleCat() { cout << "调用构造函数SimpleCat::SimpleCat()"<<endl; } SimpleCat::SimpleCat(SimpleCat&) { cout << "调用复制构造函数SimpleCat::SimpleCat(SimpleCat&)" << endl; } SimpleCat::~SimpleCat() { cout << "调用析构函数SimpleCat::~SimpleCat()" << endl; } const SimpleCat* const Func1(const SimpleCat* const theCat); int main() { cout << "0. 初始化一个SimpleCat对象Frisky" << endl; SimpleCat Frisky; cout << endl << "1. 调用Funct1函数(传const指针)" << endl; Func1(&Frisky); return 0; } const SimpleCat* const Func1(const SimpleCat* const theCat) { cout << "调用Func1函数(传const指针)(const SimpleCat* const theCat)" << endl; return theCat; } //0. 初始化一个SimpleCat对象Frisky //调用构造函数SimpleCat::SimpleCat() //1. 调用Funct1函数(传const指针) //调用Func1函数(传const指针)(const SimpleCat* const theCat) //调用析构函数SimpleCat::~SimpleCat()
1.3 作为指针替代品的引用
-
使用引用比使用指针要简单,而付出的代价和获得的效率没变,且和 const 一样安全
class SimpleCat { public: SimpleCat(); SimpleCat(SimpleCat&); ~SimpleCat(); }; SimpleCat::SimpleCat() { cout << "调用构造函数SimpleCat::SimpleCat()"<<endl; } SimpleCat::SimpleCat(SimpleCat&) { cout << "调用复制构造函数SimpleCat::SimpleCat(SimpleCat&)" << endl; } SimpleCat::~SimpleCat() { cout << "调用析构函数SimpleCat::~SimpleCat()" << endl; } const SimpleCat& Func1(const SimpleCat& theCat); int main() { cout << "0. 初始化一个SimpleCat对象Frisky" << endl; SimpleCat Frisky; cout << endl << "1. 调用Funct1函数(传引用)" << endl; Func1(Frisky); return 0; } const SimpleCat& Func1(const SimpleCat& theCat) { cout << "调用Funct1函数(传引用)const SimpleCat& Func1(const SimpleCat& theCat)" << endl; return theCat; } //0. 初始化一个SimpleCat对象Frisky //调用构造函数SimpleCat::SimpleCat() //1. 调用Funct1函数(传引用) //调用Funct1函数(传引用)const SimpleCat& Func1(const SimpleCat& theCat) //调用析构函数SimpleCat::~SimpleCat()
2. 如何确定在什么情况下使用引用以及在什么情况下使用指针
-
一般情况,
引用
更清晰,使用起来更容易 -
引用不能重新赋值,如果需要
依次指向不同的对象
,就必须使用指针
引用不能为NULL,如果
要指向的对象有可能是NULL的话
,就必须使用指针
-
如果要
从堆中分配动态内存
时,就必须使用指针
3. 使用指针时如何避免内存问题
-
程序在堆中分配内存时,将返回一个指针
必须一直让某个指针指向这块内存(指针丢失后,便无法释放该内存,进而导致内存泄露)
-
因此,在函数之间传递内存块时,分配内存块的函数将负责释放它(谁开发谁善后),然后按引用传递内存块中的值
如果让一个函数分配内存,另一个函数负责释放就很危险(忘记删除或重复删除)
4. 如何避开引用使用陷阱
-
引用始终是另一个对象的别名,按引用传递时,要注意
引用指向的对象还在不在
-
返回指向堆中对象的引用
class SimpleCat { public: SimpleCat(int a, int b); //SimpleCat(SimpleCat&); ~SimpleCat(); private: int a = 0; int b = 0; }; SimpleCat::SimpleCat(int a, int b) { cout << "调用构造函数SimpleCat::SimpleCat(int a, int b),a =" << a << " ,b =" << b << endl; }
SimpleCat::~SimpleCat()
{ cout << "调用析构函数SimpleCat::~SimpleCat()" << endl; } SimpleCat& Func1(); int main()
{ //将Func1()返回的结果赋给了一个 SimpleCat 引用 & rname(指向堆中对象的引用) SimpleCat& rname = Func1(); //该引用 & rname 指向的是Func1()堆中地址,这与堆中对象的地址相同 cout << "&rname: " << &rname<< endl; //如果想释放内存,不能对引用调用 delete //便创建另一个指针 * pCat 指向引用 &rname 的地址,这确实可以释放内存,但 &rname 指向空对象 SimpleCat* pCat = &rname; delete pCat; return 0; } SimpleCat& Func1()
{ //从堆中分配内存 new SimpleCat(),并让一个指针 * pFrisky 指向它 SimpleCat* pFrisky = new SimpleCat(5, 9); //输出该指针存储的地址 cout << "pFrisky: " << pFrisky << endl; //解引用,并按引用将 SimpleCat 对象返回 return *pFrisky; } //调用构造函数SimpleCat::SimpleCat(int a, int b),a =5 ,b =9 //pFrisky: 00000184C8254580 //& rname: 00000184C8254580 //调用析构函数SimpleCat::~SimpleCat()
对于上诉问题的解决办法
//将 Func1() 的返回类型声明为指针(而非引用),并返回指针(而非指针的解引用) SimpleCat* Func1() { //不变 SimpleCat* pFrisky = new SimpleCat(5, 9); //不变 cout << "pFrisky: " << pFrisky << endl; //返回指针 return pFrisky; }
5.总结
-
虽然但是,还是要说:
指针
是一个存储内存地址的变量
,而引用
是别名
(多数情况下,引用更好使)
-
如果返回的是局部对象,必须
按值返回
,否则返回的引用将指向不存在的对象按引用返回
的效率高、节省内存且程序运行速度更快