我很快想出了一个解决方案,我想这对于Java程序员来说是典型的:接口(=纯虚拟基类).例如:
BananaTree.h:
class Banana; class BananaTree { public: virtual Banana* getBanana(std::string const& name) = 0; static BananaTree* create(std::string const& name); };
BananaTree.cpp:
class BananaTreeImpl : public BananaTree { private: string name; Banana* findBanana(string const& name) { return //obtain banana,somehow; } public: BananaTreeImpl(string name) : name(name) {} virtual Banana* getBanana(string const& name) { return findBanana(name); } }; BananaTree* BananaTree::create(string const& name) { return new BananaTreeImpl(name); }
这里唯一的麻烦就是我不能使用new,而必须调用BananaTree :: create().我不认为这确实是一个问题,特别是因为我希望无论如何都要使用工厂.
然而,现在,C成名的聪明人提出了另一个解决方案,pImpl idiom.有了这个,如果我理解正确,我的代码看起来像:
BananaTree.h:
class BananaTree { public: Banana* addStep(std::string const& name); private: struct Impl; shared_ptr<Impl> pimpl_; };
BananaTree.cpp:
struct BananaTree::Impl { string name; Banana* findBanana(string const& name) { return //obtain banana,somehow; } Banana* getBanana(string const& name) { return findBanana(name); } Impl(string const& name) : name(name) {} } BananaTree::BananaTree(string const& name) : pimpl_(shared_ptr<Impl>(new Impl(name))) {} Banana* BananaTree::getBanana(string const& name) { return pimpl_->getBanana(name); }
这意味着我必须为BananaTree的每个公共方法实现装饰器式转发方法,在本例中为getBanana.这听起来像是我不想要的复杂性和维护工作的附加级别.
解决方法
使用虚拟基类,您可以打破一些人们对行为良好的C类所期望的语义:
我希望(或甚至要求)在堆栈上实例化类,如下所示:
BananaTree myTree("somename");
否则,我失去了RAII,我必须手动开始跟踪分配,这会导致很多麻烦和内存泄漏.
我也希望复制课程,我可以简单地做到这一点
BananaTree tree2 = mytree;
当然,除非通过将复制构造函数标记为私有来禁止复制,在这种情况下,该行甚至不会编译.
在上面的例子中,我们显然遇到的问题是你的接口类并没有真正有意义的构造函数.但是如果我尝试使用上面例子中的代码,我也会遇到很多切片问题.
对于多态对象,通常需要保持指针或对象的引用,以防止切片.在我的第一点中,这通常是不可取的,并且使内存管理更加困难.
您的代码的读者是否会理解BananaTree基本上不起作用,他必须使用BananaTree *或BananaTree&代替?
基本上,你的界面与我们喜欢的现代C一样不能很好地发挥作用
>尽可能避免指针,并且
> stack-allocate所有对象都有利于自动生命周期管理.
顺便说一下,您的虚拟基类忘记了虚拟析构函数.这是一个明显的错误.
最后,我有时用来减少样板代码量的pimpl的一个更简单的变体是让“外部”对象访问内部对象的数据成员,这样就可以避免重复接口.外部对象上的函数只是直接从内部对象访问它需要的数据,或者它调用内部对象上的辅助函数,该函数在外部对象上没有等效函数.
在你的例子中,你可以删除函数和Impl :: getBanana,而是像这样实现BananaTree :: getBanana:
Banana* BananaTree::getBanana(string const& name) { return pimpl_->findBanana(name); }
那么你只需要实现一个getBanana函数(在BananaTree类中)和一个findBanana函数(在Impl类中).