2011-06-13 60 views
1

我有一个具有2个纯虚拟方法的类和需要使用此类对象的另一个类。我想让这个类的用户指定应该在哪个派生类中使用抽象类。 我正在努力弄清楚正确的方法。在另一个类中使用抽象类对象的C++设计模式

struct abstract { 
    virtual int fst_func() = 0; 
    virtual void sec_func(int) = 0; 
}; 

// store an instance of "abstract". 
class user_of_abstract 
{ 
private: 
    abstract* m_abstract; 

public: 
    // Pass a pointer to an "abstract" object. The caller takes care of the memory resource. 
    user_of_abstract_base(abstract* a) : m_abstract(a) { } 

    // Pase any type, which needs to be derived from "abstract" and create a copy. Free memory in destructor. 
    template<class abstract_type> 
    user_of_abstract_base(abstract_type const& a) : m_abstract(new abstract_type(a)) { } 

    // use the stored member to call fst_func. 
    int use_fst_func() { 
     return this->m_abstract->fst_func(); 
    } 

    // use the stored member to call sec_func. 
    void use_sec_func(int x) { 
     this->m_abstract->sec_func(x); 
    } 
}; 

// use boost::shared_ptr 
class user_of_abstract 
{ 
private: 
    boost::shared_ptr<abstract> m_abstract; 

public: 
    // Pass a pointer to an "abstract" object. The caller takes care of the memory resource. 
    user_of_abstract_base(boost::shared_ptr<abstract> a) : m_abstract(a) { } 

    // use the stored member to call fst_func. 
    int use_fst_func() { 
     return this->m_abstract->fst_func(); 
    } 

    // use the stored member to call sec_func. 
    void use_sec_func(int x) { 
     this->m_abstract->sec_func(x); 
    } 
}; 

// pass a pointer of an "abstract" object wherever needed. 
struct user_of_abstract 
{ 
    // use the passed pointer to an "abstract" object to call fst_func. 
    int use_fst_func(abstract* a) { 
     return a->fst_func(); 
    } 

    // use the passed pointer to an "abstract" object to call sec_func. 
    void use_sec_func(abstract* a, int x) { 
     a->sec_func(x); 
    } 
}; 

要注意从sec_func该参数的“X”()是很重要的必须是同一个“抽象”的实例()由fst_func返回的值。

编辑: 增加了另一种方法使用boost :: shared_ptr应该采取最大的优势。

+1

我认为你的第二类的名字应该是'user_of_abstract_base'。 – 2011-06-13 05:50:42

+0

不要忘了你需要一个虚拟的析构函数来抽象! – 2011-06-13 05:56:20

+0

@Billy |是的,谢谢你的提示。我已经缩短了相关部分的代码。 – 0xbadf00d 2011-06-13 06:06:43

回答

1

我要说的是,经过abstract对象插入到用户的构造函数是用户的方法取决于被称为同abstract对象的正确方法。我甚至会走得更远,并且使x参数成为用户的内部状态,因为您已经说过,重要的是该值是从第一个函数调用返回的值。

更新:如果您担心生命期,那么您可以利用各种智能指针选项,例如提升。这些应该覆盖大多数使用场景。

+0

这是正确的,但您需要确保传入的实例至少和“user_of_abstract”对象具有相同的生命周期,这是我唯一担心的,另外,它不是那么方便,因为你需要两次持有相同的实例。在“user_of_abstract”对象之外的“抽象”实例。关于内部状态:请注意我对Cem Kalyoncu的评论。 – 0xbadf00d 2011-06-13 06:14:43

+0

@ FrEEzE2046更新了我的答案。 – ChrisWue 2011-06-13 07:13:36

+0

您是否看到我最初的帖子的更新?我已经展示了boost :: shared_ptr的解决方案;) – 0xbadf00d 2011-06-13 07:29:31

1

既然你说第二个函数应该使用第一个的输出。我想第一种方法会减少错误的机会。你甚至可以将它修改为以下内容:

int use_fst_func() { 
    return x=this->m_abstract->fst_func(); 
} 

void use_sec_func() { 
    this->m_abstract->sec_func(x); 
} 

protected: 
    int x; 
+0

我不能这样做,因为fst_func和sec_func被多次调用,并且fst_func的结果并不总是相同的(实际上,它永远不会返回两次相同的数字)。但我有一个std :: set <>来存储这些信息。 – 0xbadf00d 2011-06-13 06:10:16

+0

如果你的应用程序没有速度问题,你可以在这个类中有另一个集合,如果提交了一个非获取值,你可以引发一个异常。这将节省大量的调试时间。此外,只要在调试模式下执行此操作,并且在发布时不会执行任何检查,您也可以将此检查条件化。 – 2011-06-13 06:14:11

+0

我想你已经误解了我。我使用的std :: set正在做这种工作。抱歉不清楚。 – 0xbadf00d 2011-06-13 06:18:20

0

你让自己陷入了维护困境。

在你的第一个例子...

真的没有必要为模板的构造函数。它speced作为

// Parse any type, which needs to be derived from "abstract" and create a copy. 

用户已经可以做到通过创建实例自己,并把它传递给第一个构造函数。

另外,在本:

// Free memory in destructor. 

你明确地说,你不知道这个类应该如何使用。在编写第一个示例时,您需要确定:使用从外部创建的实例或使用在内部创建的实例。看到一个接口与一个ctor接受一个指针,另一个ctor接受一个引用,这两个接口都是混淆的,基本上都是相同的类型。

在我看来,使用从外部创建的实例的唯一可接受的方式是不存储管理的从内部创建的将由内存管理的实例是当存在默认的ctor可以初始化内部指针,以一个合理的值(但似乎并不在这里是如此,因为要复制另一个实例):

template <typename T> 
class user_of_abstract 
{ 
    bool m_owner_; 
    abstract* m_abstract; 

public: 

    user_of_abstract_base(abstract* a = NULL) 
    : m_owner(a == NULL) 
    , m_abstract(m_owner ? new T(): a) 
    { 
    } 

    ~user_of_abstract_base() 
    { 
     if (m_owner) 
     { 
      delete m_abstract; 
     } 
    } 
} 

你的第二个例子......

优于第一个,因为您没有明确地将内存管理与内存引用混合在一起。你让shared_ptr隐式做。非常好,就是这样。

但是,由于您要求use_sec_func必须以use_fst_func的输出作为输入,所以您可以远离安全的海洋维护问题。

例如,如果实例上的use_fst_func引发异常,并且稍后在同一实例上调用use_sec_func会发生什么情况?

您如何看待重要信息“在B之前始终呼叫A并且只有一次,并将A结果传递给B”。应该从现在起2年向班级的用户传播?

为什么不能use_sec_func只需拨打use_fst_func

至于你的第三个例子...

你可以给1分单的情况,当你想使用这个,而不是仅仅直接调用该实例的功能呢?

+0

与第三个示例相关:这就是std :: allocator_traits <>(C++ 0x)的作用...... – 0xbadf00d 2011-06-13 19:19:06

+0

另外:您可以将use_fst_func视为获取资源并使用use_fst_func释放它的内容。我认为你已经不知道调用release(acquire())或删除新的int是没有任何意义的... – 0xbadf00d 2011-06-13 19:28:41

+0

@ FrEEzE2046:*你可以把use_fst_func当作获取资源的东西,use_fst_func释放它*哦,天啊,不!绝对不!那里的例外安全在哪里? – 2011-06-13 21:09:43