C++在另一个类中使用抽象类对象的设计模式

发布于 2024-11-14 21:59:57 字数 2044 浏览 1 评论 0原文

我有一个带有 2 个纯虚方法的类和另一个需要使用此类对象的类。我想允许此类的用户指定应在其中使用抽象类的哪个派生。 我正在努力找出正确的方法是什么。

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 的方法,该方法应该发挥最大的优势。

I'm having a class with 2 pure virtual methods and another class which needs to use an object of this class. I want to allow the user of this class to specify which derivation of the abstract class should be used inside of it.
I'm struggling to figure out what the right way is.

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);
    }
};

It's important to note that parameter "x" from sec_func() needs to be a value returned by fst_func() on the same "abstract" instance.

EDIT:
Added another approach using boost::shared_ptr which should take the most advantages.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

你是我的挚爱i 2024-11-21 21:59:57

我想说,将 abstract 对象传递到用户的构造函数中是正确的方法,因为用户的方法依赖于在同一个 abstract 对象上调用。我什至会更进一步,将 x 参数设置为用户的内部状态,正如您所说,该值是从第一个函数调用返回的值,这一点很重要。

更新:如果您担心生命周期,那么您可以使用 boost 等中提供的各种智能指针选项。这些应该涵盖大多数使用场景。

I would say that passing the abstract object into the constructor of your user is the proper approach as the methods of the user depend being called on the same abstract object. I would even go further and make the x parameter an internal state of your user as you have said it's important that this value is the one returned from a call from the first function.

Update: If you are worried about the lifetimes then you could make use of the various smart pointer options available in for example boost. Those should cover most usage scenarios.

止于盛夏 2024-11-21 21:59:57

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

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

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

protected:
   int x;

Since you say the second function should use the output of the first. I guess first approach will decrease chance of mistakes. You can even modify it to the following:

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

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

protected:
   int x;
开始看清了 2024-11-21 21:59:57

您正让自己陷入维护麻烦的海洋之中。

在您的第一个示例中...

确实不需要模板构造函数。它被指定为

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

用户已经可以通过自己创建实例并将其传递给第一个构造函数来做到这一点。

另外,这样:

// Free memory in destructor.

您明确表示您不知道应该如何使用这个类。在编写第一个示例时,您需要决定:使用从外部创建的实例还是使用在内部创建的实例。看到一个接口,一个 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个场景吗?

You're putting yourself in a sea of maintenance trouble.

In your first example...

there's really no need for the template constructor. It's speced as

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

The user can already do that by creating the instance himself and pass it to the first constructor.

Also, with this:

// Free memory in destructor.

You explicitly say that you have no idea how this class should be used. As your first example is written, you need to decide: use an instance created from the outside or use an instance created on the inside. It's confusing to see an interface with one ctor taking a pointer and another ctor taking a reference, both essentially to the same type.

In my eyes, the only acceptable way of using an instance created from the outside that will not be memory-managed or an instance created from the inside that will be memory-managed, is when there's a default ctor that can initialize the internal pointer to a sensible value (but that doesn't seem to be the case here, since you want to copy another instance):

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;
        }
    }
}

Your second example...

is superior to the first, since you don't explicitly mix memory management with memory reference. You let shared_ptr do it implicitly. Very good, that's what it's for.

However, since you have a requirement that use_sec_func must take the output of use_fst_func as input, you stay a long way from the safe shore of the sea of maintenance problems.

For instance, what happens if use_fst_func on an instance throws an exception and use_sec_func is later called on that same instance?

How do you expect that the important information "Always call A before B. And only once. And pass the A result to B." should propagate to users of the class 2 years from now?

Why can't use_sec_func just call use_fst_func?

As for your third example...

can you give 1 single scenario when you'd want to use this instead of just calling the instance functions directly?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文