【Exceptional C++(14)】编译期的依赖性
2018-01-29 本文已影响14人
downdemo
问题
- 下面的头文件中哪些#include语句可以在不对程序产生副作用的前提下去掉
// gotw007.h
#include "a.h" // class A
#include "b.h" // class B
#include "c.h" // class C
#include "d.h" // class D
// 只有A和C有虚函数
#include <iostream>
#include <ostream>
#include <sstream>
#include <list>
#include <string>
class X : public A {
public:
X(const C&);
D Function1(int, char*);
D Function1(int, C);
B& Function2(B);
void Function3(std::ostream&) const;
std::ostream& print(std::ostream&) const;
private:
std::string name_;
std::list<C> clist_;
D d_;
};
std::ostream& operator<<(std::ostream& os, const X& x)
{ return x.print(os); }
class Y : private B {
public:
C Function4(A);
private:
std::list<std::wostringstream*> alist_;
};
解答
- 可以直接去掉的
- iostream,尽管用到了流,但没有用到iostream里特定的东西
- ostream和sstream,换成iosfwd
- 不能直接去掉的
- a.h,因为A是X的基类
- b.h,因为B是Y的基类
- c.h,因为list<C>需要看到C的定义
- d.h,list,string,因为X需要知道D和string的大小,X和Y需要知道list的大小
- 再来考虑通过隐藏X和Y的实现细节可以去掉的头文件
- 通过让X和Y使用pimlp_的方法去掉d.h,list和string,即私有部分被一个指针代替,这时X和Y都不再需要知道list、D或者string的大小,同理也可以这样去掉c.h,因为这时在X::clist中的C对象只作为参数或返回值出现
- B是Y的private基类,且B没有虚函数,因此b.h也可以去掉,使用private继承说明想重写虚函数,与其让Y继承B不如让Y拥有类型为B的成员,要去掉b.h应该让Y的B类型成员存在于Y中隐藏的pimlp_部分
- 使用pimpl_把代码的调用者和实现者分离,声明一个类时要避免暴露私有成员,应该使用一个struct xxxImpl *pimpl_的不透明指针来存储私有成员
- 现在还不能动a.h,A被用作public基类,含有虚函数。然而X和Y两个类之间没有任何关系,因此至少可以把X和Y的定义分别放到两个头文件中(另外还要让现有的头文件包含x.h和y.h),这样至少可以让y.h不包含a.h,因为它只把A用作函数参数的类型,不需要A的定义
//x.h
#include "a.h"
#include <iosfwd>
class C;
class D;
class X : public A {
public:
X(const C&);
D Function1(int, char*);
D Function1(int, C);
B& Function2(B);
void Function3(std::wostringstream&);
std::ostream& print(std::ostream&) const;
private:
class XImpl* pimpl_;
};
inline std::ostream& operator<<(std::ostream&os, const X& x)
{ return x.print(os); }
// y.h,没有#include
class A;
class C;
class Y {
public:
C Function4(A);
private:
class TImpl* pimpl_;
};
// gotw007.h作为存根包含两个#include
// 通过x.h又附带了另外两个
#include "x.h"
#include "y.h"
// gotw007.cpp中的新结构
// impl对象应该在X和Y的构造函数中用new创建
// 并在X和Y的析构函数中用delete清除
// X和Y的成员函数要通过pimpl_指针访问数据
struct XImpl
{
std::string name_;
std::list<C> clist_;
D d_;
};
struct YImpl
{
std::list<std::wostringstream*> alist_;
B b_;
};
// 现在,X和Y的使用者只要包含a.h和iosfwd
// 即使后来更新代码需要包含y.h并去掉gotw007.h
// 也不用加一行#include