如何解决类的相互包含,实现模块的独立

在软件设计过程中,到处充满着陷阱,程序结构设计的不合理,会让你花费成倍的时间在一个死胡同里苦苦的寻找出路。下面详细解释一下如何解决常见的类相互包含的解决方法,使得模块的完全独立。

假设有两个类classA 、classB。classA 中包含classB。例如

 

#include "classB.h"

#include <iostream>

 

using namespace std;

 

class classA

{

private:

  classB cb;

 

public:

  int show(int a);

};

 

int classA::show(int a)

{

  cout << a << endl;

}

 

在classB中有一个函数需要获取classA的某属性a.例如

 

class classA;           // 前置声明

class classB

{

private:

 

public:

  void showA();

};

 

void classB:showA()

{

  //调用classA中的show函数 (我们需要完成的功能)

}

 

如何实现这个功能呢

最简单的办法是在classB的头文件中添加一行前置声明,

 class classA;

然后在classB的定义文件中添加一个包含头文件

 include "classA.h"

这样classB便可以声明并且使用classA的对象。

 

这样看起来似乎能解决问题,但是不知不觉中就陷入了陷阱当中。因为我们一厢情愿的在classB中include了一个它的父类的头文件。

这实在不是一个明智之举,一旦classB需要做成一个单独的模块,如何解耦将会是一个问题。


由此分析可以看出,我们不应该在classB中去包含一个classA的头文件。

新的解决方法产生了,我们换一种思路,于是可以这样考虑。

在classB中提供一个函数接口,接收一个函数指针,而classA中有一个注册函数,这个注册函数会调用classB提供的这个接口,把

classA的一个函数地址注册到classB中去,这样在调用classB::showA()函数时,可以让它实际去执行这个函数指针所指向的

classA::show()函数。实现方法如下。

 

 

========================classB.h===============================

 

class classB

{

private:

  int (classA::*ptr)(int);      // 声明一个classA的成员函数指针 ,函数参数和返回值类型都为int

  classA *ca; 

 

public:

  void showA();

  // 提供注册的函数接口,由于类成员函数指针是相对于类的偏移量,所以必须传递一个类指针才能正确调用函数指针所指向的方法

  void setPtr( classA *a, int (classA::*p) (int) );

};

 

void classB:showA()

{

  //调用classA中的show函数 (我们需要完成的功能)

  (ca->*p)(1);                     // 实现调用,注意前面一对括号不能少,否则会因为操作符的优先级导致链接错误

}

 

void classB::setPtr( classA *a, int (classA::*p)(int) )

{

  ca = a;

  ptr = p;

}

 

 

========================classA.h===============================

#include "classB.h"

#include <iostream>

 

using namespace std;

 

class classA

{

private:

  classB cb;

 

public:

  int show(int a);

  void regist();

  classB getB();

};

 

classB getB()

{

  return cb;

}

 

int classA::show(int a)

{

  cout << a << endl;

}

 

void classA::regist()       // 注册函数

{

  cb.setPtr(&classA::show);   // 传入函数指针

}

 

========================main.cpp===============================

 

int main()

{

   A a;

   a.regist();

   a.getB().showA();  // 于是当调用A.getB().showA()时,实际调用的是A中的show()成员函数。

}

 

当然到目前为止并没有解决问题,大家仔细看了就会发现这种方式仍然需要在classB中include "classA.h",因为

在classB中不仅需要声明classA的类型指针,还要声明classA的成员函数指针。那该怎么办呢?

template将会帮助我们解决这个问题!

我们可以用template 来替代classB中的classA类型。

对classB.h进行修改

======================修改后的classB.h===============================

template < class T>

class classB

{

private:

  int ( T::*ptr)(int);      // 声明一个classA的成员函数指针 ,函数参数和返回值类型都为int

  T *ca; 

 

public:

  void showA();

  // 提供注册的函数接口,由于类成员函数指针是相对于类的偏移量,所以必须传递一个类指针才能正确调用函数指针所指向的方法

  void setPtr( T*a, int ( T::*p) (int) );

};

 

template < class T>

void classB<T>:showA()

{

  //调用classA中的show函数 (我们需要完成的功能)

  (ca->*p)(1);                     // 实现调用,注意前面一对括号不能少,否则会因为操作符的优先级导致链接错误

}

 

template < class T>

void classB<T>::setPtr( T*a, int (T::*p)(int) )

{

  ca = a;

  ptr = p;

}

 

 

 

========================修改后的classA.h===============================

#include "classB.h"

#include <iostream>

 

using namespace std;

 

class classA

{

private:

  classB cb;

 

public:

  int show(int a);

  void regist();

  classB<classA> getB();

};

 

classB<classA> getB()

{

  return cb;

}

 

int classA::show(int a)

{

  cout << a << endl;

}

 

void classA::regist()       // 注册函数

{

  cb.setPtr(&classA::show);   // 传入函数指针

}

 

 

注意:因为模板不是函数,它们不能单独编译,模板必须与特定的模板实例化请求一起使用。最简单的方法是将所有模板信息放在一个头文件重,并在要使用这些模板的文件中包含该头文件。如果编译器实现了新的export关键字, 则可以将模板方法定义在一个独立的文件中,条件是每个模板声明都以export开始。

例如

export template <class T>

class classB

{

...

};

 

 

至此我们就完全可以把类classB完全分离出来,即使再复杂的逻辑我们也可以用类似的方法来解决。降低模块间的耦合度。设计健壮性强、易扩展的程序来。

 

 

 

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值