前言

析构函数其实挺好猜,毕竟就一个,也没有重载。


正文

析构函数

函数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开始实例化,调用规则也是先执行基类的构造,析构从自己开始。

其实就是跟内存分配有关,派生类要构造,肯定要在基类构造完的前提下,释放的是时候因为派生类实例化,所以从他自己开始释放这也没问题。


重名问题

当基类和派生类的成员函数同名时,要分为两种情况

  1. 函数名相同,参数不同
    1. using 基类::函数名
  2. 函数名相同,参数相同
    1. 基类::函数名
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; //成员变量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; //成员变量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(){ //get
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(){ //get
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(){ //getx
return x;
}
int getX(int n){ //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(){ //getx
return x;
}
int getX(int n){ //getx重载
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的借用根据情况而定。


结语

虽然看完写下来还是迷迷糊糊的感觉,但是大致上有方向了。