静态连编与动态连编

C++中的动态连编和静态连编

1. 什么是连编?

  • 将一个调用函数连接上正确的被调用函数,这个过程就叫做函数的联编,简称联编。

2. 在C++中存在如下两种联编的方式:

  • 静态联编
    • 定义:静态联编是指联编工作出现在编译连接阶段
    • 特点:① 静态联编就是指被调用函数和执行调用函数之间的关系以及它们在内存中的地址在编译的时候已经确定好了,运行时不会发生变化。②由于对象不用对自身进行跟踪,因此速度浪费比较小,但是灵活性较差。
  • 动态联编
    • 定义:动态联编是指在程序运行的时候才进行的联编工作。
    • 特点:① 由于编译程序在编译阶段并不能确切知道将要调用的函数,只有在程序执行时才能确定将要调用的函数。要确切之道该调用的函数,就必须要求联编工作在程序运行时才能进行。 ② 虽然可以追踪对象,灵活性较强,但是速度浪费严重。

3. 在C++中,出现多态有三个条件:

  • 继承
  • virtual关键字修饰
  • 父类指针或者引用指向子类对象

virtual关键字的作用是,告诉编译器,我这个函数是用来支持多态的虚函数,你不要在编译期间就确定怎么来调用我,而是要在运行的时候,根据对象的实际类型,去判断如何调用.

4. 重载与重写

  • 重载:
    • 函数在同一作用域里
    • 函数名相同/参数不同
    • 返回值可以不同
  • 重写(覆盖)
    • 函数不在同一作用域里(两个函数分别在父类和子类里)
    • 函数名相同/参数列表相同/返回值相同(协变除外)
    • 基类函数必须是虚函数(有关键字virtual)
    • 访问修饰符可以不同

5. 子类新增加方法和父类有啥关系?

  • 子类新增的方法,子类自己可以访问,但是通过父类的引用,指向子类的对象,或者父类的指针,指向子类的对象,都不可以访问….

6. 怎么解释,编译看父类,运行看子类?

  • 个人的理解就是,我们用父类的引用接收他的子类的对象的时候,我们当前的这个引用的功能是比较强大的,n多个子类对象重写这个方法,这个方法就有n多个功能

  • 过程:

    1. 编译一开始看看自己有没有这个方法.如果是普通的函数,而不是虚函数,编译的时候就知道了要调用 方法,就是这个方法,(静态连编)…
    2. 如果是虚函数,查看子类有没有重写他的方法,没有的话,调用自己的方法,如果子类重写了,调用的是子类的方法;(动态连编)…
    3. 他可能不止有一个子类,假设有很多个子类,都重写的他的方法,那么具体调用的是这个引用指向的那个子类的方法…

7. 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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include"iostream"
using namespace std;
#include"stdlib.h"

class A
{
public:
virtual void func1()
{
cout << "A::func1()" << endl;
}
virtual void func1(int x)//在B里被隐藏(在B里),指定作用域调用
{
cout << "A::func1(int x)" << endl;
}

void func2()//在B里被隐藏(在B里),指定作用域调用
{
cout << "A::func222()" << endl;
}

void func3()//在B里被隐藏(在B里),指定作用域调用
{
cout << "A::func333()" << endl;
}
virtual void func4()//在B里被隐藏(在B里),指定作用域调用
{
cout << "A::func444()" << endl;
}
};

class B:public A
{
public:
void func1()
{
cout << "B::func1 重写的父类虚函数" << endl;
}

void func2()
{
cout << "B::func2222 重写的普通函数" << endl;
}

void func4()
{
cout << "B::func444 重写的父类的虚函数" << endl;
}

void func5()
{
cout << "B::func555 子类新增的,父类中没有..." << endl;
}
};


void Test()
{
A a;
B b;

cout<<"子类对象直接调用自己的方法"<<endl;
b.func1();//静态链编
// b.func1(10);//编译不过
cout<<endl<<"子类方法通过域作用符(没有域作用符,无法直接访问),访问父类的 虚函数"<<endl;
b.A::func1(10);

cout<<endl<<"子类方法通过域作用符(没有域作用符,无法直接访问),访问父类的普通函数(自己重写了)"<<endl;
b.A::func2();

cout<<endl<<"子类方法通过域作用符(没有域作用符,无法直接访问),访问父类的普通函数(自己未重写)"<<endl;
b.A::func3();
cout<<"-----"<<endl<<endl;

cout<<"父类指针指向父类对象(正常调用他自己的方法)"<<endl;
A* p1 = &a;
p1->func1();
p1->func1(10);
p1->func2();
p1->func3();


cout<<endl<<"子类指针指向子类对象(既可以调用自己方法,也可以直接调用父类的方法)"<<endl;
B* p2 = &b;
p2->func1();//动态链编,到虚表里找,找到自己的func1()
//p2->func1(10);
p2->func2();
p2->A::func1(10);
p2->A::func2();
p2->A::func3();

cout<<endl<<"--父类指针指向子类对象- 如果父类的这个方法是虚函数,并且子类重写了它,那么执行的是子类的方法,--动态连编--"<<endl;
cout<<"这种写法,不可以访问,子类新增的,但是父类中却没有的方法..."<<endl;
A* p3 = &b;
p3->func1();
p3->func1(10);
p3->func2();
p3->func3();
p3->func4();
// p3->func5();

cout<<endl<<"--父类引用指向子类对象 (效果,原理同上)--"<<endl;
A&p4 =b;
p4.func1();
p4.func1(10);
p4.func2();
p4.func3();
p4.func4();
// p4.func5(); /*/

cout<<endl<<"子类引用,指向父类对象(编译不通过)"<<endl;
//B& m = a;

}

int main()
{
Test();
system("pause");
return 0;
}