来自同一祖父母的多重继承 - 合并实现?

发布于 2024-11-01 19:21:10 字数 1116 浏览 1 评论 0原文

对于某个项目,我声明了一个接口(一个仅具有纯虚函数的类),并希望为用户提供该接口的一些实现。

我希望用户有很大的灵活性,所以我提供了这个接口的部分实现。在每个实现中都包含一些功能,其他功能不会被覆盖,因为它们负责不同的部分。

但是,我还想向用户提供一个完全可用的界面实现。所以我的第一个方法是简单地从两个部分实现派生一个类。这不起作用并退出,并出现错误:某些函数在派生类中仍然是纯虚拟的。

所以我的问题是是否有任何方法可以简单地合并同一接口的两个部分实现。我找到了一种解决方法,明确说明我想为每个方法调用哪个函数,但我认为这非常丑陋,并且会很感激有一个机制可以为我处理这个问题。

#include <iostream>

class A{
    public:
        virtual void foo() = 0;
        virtual void bar() = 0;
};

class B: public A{
    public:
        void foo(){ std::cout << "Foo from B" << std::endl; }
};

class C: public A{
    public:
        void bar(){ std::cout << "Bar from C" << std::endl; }
};

// Does not work
class D: public B, public C {};

// Does work, but is ugly
class D: public B, public C {
    public:
        void foo(){ B::foo(); }
        void bar(){ C::bar(); }
};

int main(int argc, char** argv){
    D d;
    d.foo();
    d.bar();
}

问候, Alexander


实际的问题是管理一棵树的多个访问者,让每个访问者遍历这棵树,为每个节点做出决策,然后聚合每个访问者的决策并将其累积为一个确定的决策。

遗憾的是,如果没有(我认为)巨大的开销,这两个部分的分离是不可能的,因为我想提供一个负责管理访问者的实现,另一个负责如何存储最终决定。

for a certain project I have declared an interface (a class with only pure virtual functions) and want to offer users some implementations of this interface.

I want users to have great flexibility, so I offer partial implementations of this interface. In every implementation there is some functionality included, other functions are not overridden since they take care about different parts.

However, I also want to present users with a fully usable implementation of the interface as well. So my first approach was to simply derive a class from both partial implementations. This did not work and exited with the error that some functions are still pure virtual in the derived class.

So my question is if there is any way to simply merge two partial implementations of the same interface. I found a workaround by explicitely stating which function I want to be called for each method, but I consider this pretty ugly and would be grateful for an mechanism taking care of this for me.

#include <iostream>

class A{
    public:
        virtual void foo() = 0;
        virtual void bar() = 0;
};

class B: public A{
    public:
        void foo(){ std::cout << "Foo from B" << std::endl; }
};

class C: public A{
    public:
        void bar(){ std::cout << "Bar from C" << std::endl; }
};

// Does not work
class D: public B, public C {};

// Does work, but is ugly
class D: public B, public C {
    public:
        void foo(){ B::foo(); }
        void bar(){ C::bar(); }
};

int main(int argc, char** argv){
    D d;
    d.foo();
    d.bar();
}

Regards,
Alexander


The actual problem is about managing several visitors for a tree, letting each of them traverse the tree, make a decision for each of the nodes and then aggregate each visitor's decision and accumulate it into a definite decision.

A separation of both parts is sadly not possible without (I think) massive overhead, since I want to provide one implementation taking care of managing the visitors and one taking care of how to store the final decision.

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

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

发布评论

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

评论(5

囍孤女 2024-11-08 19:21:10

您是否考虑过完全避免钻石继承,提供几个抽象类,每个抽象类都有可选的实现,允许用户根据需要混合和匹配默认实现和接口?

在您的情况下,发生的情况是,一旦您继承到 DB::bar 尚未实现,而 C::foo 尚未实现。尚未实施。中间类 BC 无法看到彼此的实现。

如果您需要祖父母中的完整接口,您是否考虑过以不同的方式提供实现,可能是带有模板的策略,以及将被分派到提供默认行为的默认类?

Have you considered avoiding the diamond inheritance completely, providing several abstract classes each with optional implementations, allowing the user to mix and match default implementation and interface as needed?

In your case what's happening is that once you inherit to D, B::bar hasn't been implemented and C::foo hasn't been implemented. The intermediate classes B and C aren't able to see each others' implementations.

If you need the full interface in the grandparent, have you considered providing the implementation in a different way, possibly a policy with templates, and default classes that will be dispatched into to provide the default behavior?

白龙吟 2024-11-08 19:21:10

如果您的顶级接口在功能上有逻辑划分,您应该将其拆分为两个单独的接口。例如,如果接口 A 中同时具有序列化和绘图功能,则应将它们分为两个接口:ISerialization 和 IDrawing。

然后您可以自由地提供每个接口的默认实现。类的用户可以根据需要继承您的接口或默认实现。

If your top level interface has a logical division in functionality, you should split it into two separate interfaces. For example if you have both serialization and drawing functions in interface A, you should separate these into two interfaces, ISerialization and IDrawing.

You're free to then provide a default implementation of each of these interfaces. The user of your classes can inherit either your interface or your default implementation as needed.

无法言说的痛 2024-11-08 19:21:10

您还可以使用“工厂”类作为主接口类型。换句话说,主接口类还包含某种类型的静态函数,该函数根据用户的请求生成适当的子类。例如:

#include <cstdio>

class A 
{
    public:
       enum class_t { CLASS_B, CLASS_C };

       static A* make_a_class(class_t type);

       virtual void foo() = 0;
       virtual void bar() = 0;
};

class B: public A
{
    private:
        virtual void foo() { /* does nothing */ }

    public:
        virtual void bar() { printf("Called B::bar()\n"); }
};

class C: public A
{
    private:
        virtual void bar() { /* does nothing */ }

    public:
        virtual void foo() { printf("Called C::foo()\n"); }
};

A* A::make_a_class(class_t type)
{
   switch(type)
   {
       case CLASS_B: return new B();
       case CLASS_C: return new C();
       default: return NULL;
    }
}

int main()
{
    B* Class_B_Obj = static_cast<B*>(A::make_a_class(A::CLASS_B));
    C* Class_C_Obj = static_cast<C*>(A::make_a_class(A::CLASS_C));

    //Class_B_Obj->foo(); //can't access since it's private
    Class_B_Obj->bar();

    Class_C_Obj->foo();
    //Class_C_Obj->bar(); //can't access since it's private

    return 0;
}

如果class A出于某种原因需要访问class Bclass C的一些私有成员,只需将class A 子类的友元(例如,您可以将 B 类C 类 的构造函数设为私有构造函数,这样只有 中的静态函数>class A 可以生成它们,用户无法自己制作而不调用A类中的静态工厂函数)。

希望这有帮助,

杰森

There is also the possibility that you could use a "factory" class for the main interface type. In other words the primary interface class also contains some type of static function that generates an appropriate child class on-request from the user. For instance:

#include <cstdio>

class A 
{
    public:
       enum class_t { CLASS_B, CLASS_C };

       static A* make_a_class(class_t type);

       virtual void foo() = 0;
       virtual void bar() = 0;
};

class B: public A
{
    private:
        virtual void foo() { /* does nothing */ }

    public:
        virtual void bar() { printf("Called B::bar()\n"); }
};

class C: public A
{
    private:
        virtual void bar() { /* does nothing */ }

    public:
        virtual void foo() { printf("Called C::foo()\n"); }
};

A* A::make_a_class(class_t type)
{
   switch(type)
   {
       case CLASS_B: return new B();
       case CLASS_C: return new C();
       default: return NULL;
    }
}

int main()
{
    B* Class_B_Obj = static_cast<B*>(A::make_a_class(A::CLASS_B));
    C* Class_C_Obj = static_cast<C*>(A::make_a_class(A::CLASS_C));

    //Class_B_Obj->foo(); //can't access since it's private
    Class_B_Obj->bar();

    Class_C_Obj->foo();
    //Class_C_Obj->bar(); //can't access since it's private

    return 0;
}

If class A for some reason needs to access some private members of class B or class C, just make class A a friend of the children classes (for instance, you could make the constructors of class B and class C private constructors so that only the static function in class A can generate them, and the user can't make one on their own without calling the static factory function in class A).

Hope this helps,

Jason

¢好甜 2024-11-08 19:21:10

既然您提到您主要需要访问函数而不是数据成员,那么您可以使用另一种方法,而不是使用模板和模板部分专业化进行多重继承:

#include <iostream>

using namespace std;

enum class_t { CLASS_A, CLASS_B, CLASS_C };

template<class_t class_type>
class base_type
{
    public:
            static void foo() {}
            static void bar() {}
};

template<>
void base_type<CLASS_A>::foo() { cout << "Calling CLASS_A type foo()" << endl; }

template<>
void base_type<CLASS_B>::bar() { cout << "Calling CLASS_B type bar()" << endl; }

template<>
void base_type<CLASS_C>::foo() { base_type<CLASS_A>::foo(); }

template<>
void base_type<CLASS_C>::bar() { base_type<CLASS_B>::bar(); }

int main()
{
    base_type<CLASS_A> Class_A;
    Class_A.foo();

    base_type<CLASS_B> Class_B;
    Class_B.bar();

    base_type<CLASS_C> Class_C;
    Class_C.foo();
    Class_C.bar();

    return 0;
}

现在,如果您需要可以访问私有数据的非静态函数 -各位,这可能会有点棘手,但仍然是可行的。尽管它很可能需要一个单独的特征类,您可以使用它来访问正确的类型,而不会遇到“不完整类型”编译器错误。

谢谢,

杰森

Since you mentioned that you mainly needed access to the functions rather than data-members, here is another method you could use rather than multiple inheritance using templates and template partial specialization:

#include <iostream>

using namespace std;

enum class_t { CLASS_A, CLASS_B, CLASS_C };

template<class_t class_type>
class base_type
{
    public:
            static void foo() {}
            static void bar() {}
};

template<>
void base_type<CLASS_A>::foo() { cout << "Calling CLASS_A type foo()" << endl; }

template<>
void base_type<CLASS_B>::bar() { cout << "Calling CLASS_B type bar()" << endl; }

template<>
void base_type<CLASS_C>::foo() { base_type<CLASS_A>::foo(); }

template<>
void base_type<CLASS_C>::bar() { base_type<CLASS_B>::bar(); }

int main()
{
    base_type<CLASS_A> Class_A;
    Class_A.foo();

    base_type<CLASS_B> Class_B;
    Class_B.bar();

    base_type<CLASS_C> Class_C;
    Class_C.foo();
    Class_C.bar();

    return 0;
}

Now if you need non-static functions that have access to private data-members, this can get a bit trickier, but it should still be doable. It would though most likely require the need for a separate traits class you can use to access the proper types without running into "incomplete types" compiler errors.

Thanks,

Jason

昨迟人 2024-11-08 19:21:10

我认为问题在于,当在 B 和 A 之间以及 C 和 A 之间使用简单继承时,最终会在 D 中得到两个 A 类型的对象(每个对象都有一个纯虚函数,导致编译错误,因为 D 是抽象的,并且您尝试创建它的实例)。

使用虚拟继承解决了这个问题,因为它确保 D 中只有一份 A 的副本。

I think the problem is that when using simple inheritance between B and A, and between C and A, you end up with two objects of type A in D (each of which will have a pure virtual function, causing a compile error because D is thus abstract and you try to create an instance of it).

Using virtual inheritance solves the problem since it ensure there is only one copy of A in D.

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