首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C++菱形问题-如何只调用一次基方法

C++菱形问题-如何只调用一次基方法
EN

Stack Overflow用户
提问于 2019-04-24 12:14:15
回答 7查看 5.5K关注 0票数 39

我在C++中使用多重继承,并通过显式调用基方法来扩展基方法。假定以下层次结构:

代码语言:javascript
运行
复制
     Creature
    /        \
 Swimmer    Flier
    \        /
       Duck

对应于

代码语言:javascript
运行
复制
class Creature
{
    public:
        virtual void print()
        {
            std::cout << "I'm a creature" << std::endl;
        }
};

class Swimmer : public virtual Creature
{
     public:
        void print()
        {
            Creature::print();
            std::cout << "I can swim" << std::endl;
        }
};

class Flier : public virtual Creature
{
     public:
        void print()
        {
            Creature::print();
            std::cout << "I can fly" << std::endl;
        }
};

class Duck : public Flier, public Swimmer
{
     public:
        void print()
        {
            Flier::print();
            Swimmer::print();
            std::cout << "I'm a duck" << std::endl;
        }
};

现在出现了一个问题--调用鸭子的print方法调用其各自的基本方法,所有这些方法依次调用Creature::print()方法,因此它最终被调用了两次-

代码语言:javascript
运行
复制
I'm a creature
I can fly
I'm a creature
I can swim
I'm a duck

我想找到一种方法来确保基法只被调用一次。类似于虚拟继承的工作方式(在第一次调用时调用基本构造函数,然后只在来自其他派生类的连续调用时分配指向它的指针)。

是否有一些内置的方式来做到这一点,或者我们是否需要自己实施?

如果是的话,你会如何处理这个问题?

这个问题并不是专门针对印刷的。我想知道是否有一种机制可以在保持调用顺序的同时扩展基本方法和功能,并避免菱形问题。

我现在知道最突出的解决方案是添加助手方法,但我只是想知道是否有一种“更干净”的方法。

EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2019-04-25 11:23:14

您请求的是类似于函数级别上的继承,它会自动调用继承的函数并只添加更多的代码。另外,您希望以虚拟的方式完成,就像类继承一样。伪语法:

代码语言:javascript
运行
复制
class Swimmer : public virtual Creature
{
     public:
        // Virtually inherit from Creature::print and extend it by another line of code
        void print() : virtual Creature::print()
        {
            std::cout << "I can swim" << std::endl;
        }
};

class Flier : public virtual Creature
{
     public:
        // Virtually inherit from Creature::print and extend it by another line of code
        void print() : virtual Creature::print()
        {
            std::cout << "I can fly" << std::endl;
        }
};

class Duck : public Flier, public Swimmer
{
     public:
        // Inherit from both prints. As they were created using "virtual function inheritance",
        // this will "mix" them just like in virtual class inheritance
        void print() : Flier::print(), Swimmer::print()
        {
            std::cout << "I'm a duck" << std::endl;
        }
};

所以你的问题的答案

有没有什么内置的方法来做这件事?

就是不。在C++中不存在这样的东西。而且,我不知道有任何其他语言有类似的东西。但这是个有趣的主意..。

票数 3
EN

Stack Overflow用户

发布于 2019-04-24 12:29:56

很可能这是一个XY问题。但是..。别叫两次。

代码语言:javascript
运行
复制
#include <iostream>

class Creature
{
public:
    virtual void identify()
    {
        std::cout << "I'm a creature" << std::endl;
    }
};

class Swimmer : public virtual Creature
{
public:
    virtual void identify() override
    {
        Creature::identify();
        tell_ability();
        std::cout << "I'm a swimmer\n";
    }

    virtual void tell_ability()
    {
        std::cout << "I can swim\n";
    }
};

class Flier : public virtual Creature
{
public:
    virtual void identify() override
    {
        Creature::identify();
        tell_ability();
        std::cout << "I'm a flier\n";
    }

    virtual void tell_ability()
    {
        std::cout << "I can fly\n";
    }
};

class Duck : public Flier, public Swimmer
{
public:
    virtual void tell_ability() override
    {
        Flier::tell_ability();
        Swimmer::tell_ability();
    }

    virtual void identify() override
    {
        Creature::identify();
        tell_ability();
        std::cout << "I'm a duck\n";
    }
};

int main()
{
    Creature c;
    c.identify();
    std::cout << "------------------\n";

    Swimmer s;
    s.identify();
    std::cout << "------------------\n";

    Flier f;
    f.identify();
    std::cout << "------------------\n";

    Duck d;
    d.identify();
    std::cout << "------------------\n";
}

输出:

代码语言:javascript
运行
复制
I'm a creature
------------------
I'm a creature
I can swim
I'm a swimmer
------------------
I'm a creature
I can fly
I'm a flier
------------------
I'm a creature
I can fly
I can swim
I'm a duck
------------------
票数 50
EN

Stack Overflow用户

发布于 2019-04-24 12:25:17

我们可以让基类跟踪属性:

代码语言:javascript
运行
复制
#include <iostream>
#include <string>
#include <vector>

using namespace std::string_literals;

class Creature
{
public:
    std::string const attribute{"I'm a creature"s};
    std::vector<std::string> attributes{attribute};
    virtual void print()
    {
        for (auto& i : attributes)
            std::cout << i << std::endl;
    }
};

class Swimmer : public virtual Creature
{
public:
    Swimmer() { attributes.push_back(attribute); }
    std::string const attribute{"I can swim"s};
};

class Flier : public virtual Creature
{
public:
    Flier() { attributes.push_back(attribute); }
    std::string const attribute{"I can fly"s};
};

class Duck : public Flier, public Swimmer
{
public:
    Duck() { attributes.push_back(attribute); }
    std::string const attribute{"I'm a duck"s};
};

int main()
{
    Duck d;
    d.print();
}

同样,如果它不仅仅是我们要打印的,而是函数调用,那么我们可以让基类跟踪这些函数:

代码语言:javascript
运行
复制
#include <iostream>
#include <functional>
#include <vector>

class Creature
{
public:
    std::vector<std::function<void()>> print_functions{[this] {Creature::print_this(); }};
    virtual void print_this()
    {
        std::cout << "I'm a creature" << std::endl;
    }
    void print()
    {
        for (auto& f : print_functions)
            f();
    }
};

class Swimmer : public virtual Creature
{
public:
    Swimmer() { print_functions.push_back([this] {Swimmer::print_this(); }); }
    void print_this()
    {
        std::cout << "I can swim" << std::endl;
    }
};

class Flier : public virtual Creature
{
public:
    Flier() { print_functions.push_back([this] {Flier::print_this(); }); }
    void print_this()
    {
        std::cout << "I can fly" << std::endl;
    }
};

class Duck : public Flier, public Swimmer
{
public:
    Duck() { print_functions.push_back([this] {Duck::print_this(); }); }
    void print_this()
    {
        std::cout << "I'm a duck" << std::endl;
    }
};

int main()
{
    Duck d;
    d.print();
}
票数 23
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55829798

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档