前言
析构函数其实挺好猜,毕竟就一个,也没有重载。
正文
析构函数
函数A->B->C
它的析构函数其实可以猜一猜顺序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| class father{ public: int x; int y; father(){ std::cout << "father created\n"; } father(const father &f){ std::cout << "father copy created\n"; } ~father(){ std::cout << "father delete\n"; } };
class son :public father{ public: int age; son(){ std::cout << "son created\n"; } son(const son &s) :father{s}{ std::cout << "son copy created\n"; } son(int _age) :age{ _age }{ std::cout << _age << std::endl; } ~son(){ std::cout << "son delete\n"; } };
class grandson :public son{ public: using son::son; grandson(){ std::cout << "grandson created\n"; } grandson(const grandson &g) :son{g}{ std::cout << "grandson copy created\n"; } ~grandson(){ std::cout << "grandson delete\n"; } };
|
以上一个例子,挨个写个析构函数。
结果其实显而易见,实例化的时候会先调用基类的构造,那么结束的时候肯定先从自身析构释放掉。
相同的从son开始实例化,调用规则也是先执行基类的构造,析构从自己开始。
其实就是跟内存分配有关,派生类要构造,肯定要在基类构造完的前提下,释放的是时候因为派生类实例化,所以从他自己开始释放这也没问题。
重名问题
当基类和派生类的成员函数同名时,要分为两种情况
- 函数名相同,参数不同
- using 基类::函数名
- 函数名相同,参数相同
- 基类::函数名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| class father{ public: int x; int y; father(){ std::cout << "father created\n"; } father(const father &f){ std::cout << "father copy created\n"; } ~father(){ std::cout << "father delete\n"; } };
class son :public father{ public: int x; int age; son(){ std::cout << "son created\n"; } son(const son &s) :father{s}{ std::cout << "son copy created\n"; } son(int _age) :age{ _age }{ std::cout << _age << std::endl; } ~son(){ std::cout << "son delete\n"; } };
|
当两个类中都出现相同变量名时,编译的时候肯定不会出错,这是因为作用域的问题。
当son类要调用father的x时:
直接调用毫无疑问就是自己作用域中的成员x。
而我们说原因出在作用域上,解决办法也就是加上作用域区分。
在类中定义是如此,在实例化后调用
看上去调用方式就像调用了成员然后作用域的变量一样。
当然可以选择验证一下,添加个getter函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| class father{ public: int x; int y; father(){ std::cout << "father created\n"; } father(const father &f){ std::cout << "father copy created\n"; } ~father(){ std::cout << "father delete\n"; } int getX1(){ return x; } };
class son :public father{ public: int x; int age; son(){ x = 100; father::x = 200; std::cout << "son created\n"; } son(const son &s) :father{s}{ std::cout << "son copy created\n"; } son(int _age) :age{ _age }{ std::cout << _age << std::endl; } ~son(){ std::cout << "son delete\n"; } int getX2(){ return x; } };
|
这里先搞变量重名的问题,函数先主动区分开。
此处调用的时候显示的跟我们初始化是一样的,说明代码没问题。
以后开发的时候,别人的类就很有可能是编译好之后发给你,那你就无法修改了,所以要注意自己做的时候。
那么回到函数重命名的时候。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| class father{ public: int x; int y; father(){ std::cout << "father created\n"; } father(const father &f){ std::cout << "father copy created\n"; } ~father(){ std::cout << "father delete\n"; } int getX(){ return x; } };
class son :public father{ public: int x; int age; son(){ x = 100; father::x = 200; std::cout << "son created\n"; } son(const son &s) :father{s}{ std::cout << "son copy created\n"; } son(int _age) :age{ _age }{ std::cout << _age << std::endl; } ~son(){ std::cout << "son delete\n"; }
int getX(int n){ return x; } };
|
当函数名相同,参数不同,他能看到的就只有自己作用域的。
想要访问到father的getx,就需要使用using解锁。
解锁完之后,就会形成一种重载的风格。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| class father{ public: int x; int y; father(){ std::cout << "father created\n"; } father(const father &f){ std::cout << "father copy created\n"; } ~father(){ std::cout << "father delete\n"; } int getX(){ return x; } int getX(int n){ return x; } };
class son :public father{ public: int x; int age; son(){ x = 100; father::x = 200; std::cout << "son created\n"; } son(const son &s) :father{s}{ std::cout << "son copy created\n"; } son(int _age) :age{ _age }{ std::cout << _age << std::endl; } ~son(){ std::cout << "son delete\n"; } int getX(){ return x; } int getX(int n){ return x; } };
|
对于相同的重载问题,虽然是重载,但是属于谁的,显然作用域在son里面,father的就会被覆盖掉。
要解决,using已经不够了,只能上我们的作用域。
靠作用域区分也的确达到了效果。
作用域区分已经较为熟悉了,给grandson上一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class grandson :public son{ public: int x; using son::son; grandson(){ std::cout << "grandson created\n"; } grandson(const grandson &g) :son{g}{ std::cout << "grandson copy created\n"; } ~grandson(){ std::cout << "grandson delete\n"; } int getX(){ return x; } };
|
根据层次赋值,根据层次打印。作用域区分是最为直观的。
using的借用根据情况而定。
结语
虽然看完写下来还是迷迷糊糊的感觉,但是大致上有方向了。