Decoupling by Virtual Function 通过虚函数解耦合的软件设计

为什么C++世界要虚函数以及纯虚函数,然后用父类指针指向子类?反正怎么都需要先创建子类实例对象,为何不直接使用这个子类对象,而要多此一举先赋给父类指针,再使用?
答案是 —— 为了软件模块化和程序员协作的独立性,也称解耦合。

举个例子
1)我手握一个父类指针,指向一个对象,不关心具体指向了什么对象,我只管用它调用约定好的接口(虚函数), 那我的编译就是独立的了,我可以不用等别人的代码ready,就能自由写我的代码了。
2)你写了一个子类实现这个接口,并把子类实例化对象扔给我的父类指针。你的编译也是独立的了
3)他也写一个子类实现这个接口,也把子类实例化对象扔给我的父类指针。他的编译也是独立的了
那么链接的时候,2)和3)谁后链接上,就挤掉了对方,抢占了父类指针。看下面的代码

接口定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//==========farther.h==========
#ifndef __FARTHER_H__
#define __FARTHER_H__
using namespace std;
class Farther
{
public:
Farther();
virtual void func()=0;
};
extern Farther* sonPtr;
#endif //__FARTHER_H__

//==========farther.cpp==========
#include "farther.h"
Farther* sonPtr;
Farther::Farther(){
sonPtr=this; //谁实例化谁就把自己给sonPtr
}

//==========接口可以独立编译产生 farther.o ==========
$ g++ -c farther.cpp

我的代码,主控

1
2
3
4
5
6
7
8
//==========main.cpp==========
#include "farther.h"
int main(){
sonPtr->func(); //我不管sonPtr指向谁,反正我要调用func()
}

//==========主控代码可以独立编译产生 main.o ==========
$ g++ -c main.cpp

你的代码,实现一个子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//==========sone_1.cpp==========
#include "farther.h"
#include <iostream>
using namespace std;
class Son_1: public Farther
{
public:
void func(){
cout << "This is func() from Son_1" << endl;
}
};
Son_1 son_1;

//==========可以独立编译产生 sone_1.o ==========
$ g++ -c sone_1.cpp

他的代码,实现另一个子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//==========sone_2.cpp==========
#include "farther.h"
#include <iostream>
using namespace std;
class Son_2: public Farther
{
public:
void func(){
cout << "This is func() from Son_2" << endl;
}
};
Son_2 son_2;

//==========可以独立编译产生 sone_2.o ==========
$ g++ -c sone_2.cpp

联调

1
2
3
4
5
6
$ g++ main.o farther.o son_2.o son_1.o -o main
$ ./main
//This is func() from Son_1
$ g++ main.o farther.o son_1.o son_2.o -o main
$ ./main
//This is func() from Son_2

可见解耦效果明显,大家都可以愉快地并行开发。
但是实际上这种联调方式不会在实际中发生,因为要用新功能son_2直接别链接son_1就完事了

1
$ g++ main.o farther.o son_2.o -o main

不过,也有一种情况,是我需要顺序执行所有新老功能,那可以这么做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//把接口中sonPtr改成vector以允许功能堆叠
vector<Farther*> sonPtr;
Farther::Farther(){
sonPtr.push_back(this);
}
//那么主控中就遍历执行
for(auto p:sonPtr)
p->func();

//不过链接的顺序还是要注意
$ g++ main.o farther.o son_1.o son_2.o -o main
$ ./main
//This is func() from Son_1
//This is func() from Son_2

以上。