我必须使用一个非常笨拙的界面的旧类。由于我无法改变它并依赖它,我想构建一个包装器,提供一个干净的界面。假设我有一个 ClumsyClass 类。基本上,我有三种方法:
1。参考会员
Class Wrapper {
public:
Wrapper (ClumsyClass& clumsyClass)
: m_clumsyClass(clumsyClass)
{ }
int getSmth() {
return m_clumsyClass.getSmth();
}
private:
ClumsyClass& m_clumsyClass;
}
2。指针成员
Class Wrapper {
public:
Wrapper (ClumsyClass* clumsyClass)
: m_clumsyClass(clumsyClass)
{ }
int getSmth() {
return m_clumsyClass->getSmth();
}
private:
ClumsyClass* m_clumsyClass;
}
第3。继承
Class Wrapper : public ClumsyClass {
...
}
哪种方法是实现包装器的“最干净”的方法?我更喜欢第三个,但是当我已经有一个ClumsyClass对象然后创建一个Wrapper对象(复制构造函数)时,将需要更多的内存(因为在我的情况下,原始类的实例是必需的。)
答案 0 :(得分:1)
我会避免使用3,因为它无法封装ClumsyClass
。 Wrapper
的用户可以有意或无意地直接访问"笨拙的" ClumsyClass
的界面,这是你要避免的。更喜欢继承的组合。
1.和2.之间的差异很小。使用引用成员使包装器不太灵活。该类不可分配,您不能重新引用该引用并将其替换为ClumsyClass
的其他实例,并且该成员不能为空。根据您的要求,这些可能是好的或坏的。
但正如评论中所述,默认选择应该是ClumsyClass
作为Wrapper
的按值成员:
class Wrapper {
public:
// possible constructors
//Wrapper(const ClumsyClass& cc) : m_clumsyClass(cc) {} // copy
//Wrapper(ClumsyClass&& cc) : m_clumsyClass(std::move(cc)) {} // move
int getSmth() { return m_clumsyClass.getSmth(); }
private:
ClumsyClass m_clumsyClass;
};
在您的特定用例中,为什么这可能不可能或不可取,有很多原因,然后您可以回退到选项1或2.该决定主要取决于所有权。应Wrapper
"拥有" ClumsyClass
或ClumsyClass
的实例的有效期是否超出Wrapper
?
使用直接成员的一个潜在缺点是你不能再隐藏前向声明后面ClumsyClass
的实现,这样你就失去了ClumsyClass
的一些封装。值得注意的是,解决这个问题的一种方法是提取出一个抽象的基类" interface" Wrapper
继承自。{1}}。类似的东西:
class IWrapper {
public:
virtual ~IWrapper() {}
virtual int getSmth() = 0;
};
可能会提供额外的好处,例如可测试性。
答案 1 :(得分:0)
从你提到的限制看来,这可能是一种可行的方法:
使用您想要的方法创建一个界面
struct IMyMethods
{
virtual int getOther() = 0;
...
virtual ~IMyMethods() {};
};
现在创建一个新类,派生自您的类和要在添加接口实现的类上使用的接口
class MyAgileClass : public ClumsyClass, IMyMethods
{
public:
MyAgileClass(/*same args as for ClumsyClass*/)
{}
~MyAgileClass()
{}
virtual int getOther()
{
return getSmth(); // clumsy class function
}
...
};
您现在可以直接访问ClumsyClass,也可以通过简化的界面
访问ClumsyClass