继承和2种接口类型

时间:2016-06-14 18:33:44

标签: c++ inheritance sfml

我有关于继承和设计用户界面的问题。

我有class KeyboardKey代表个人键盘键,例如Q,W,E,R等......

我有class Keyboard,其中包含 class Keyboardkey的向量。 [重要!]

我正在使用SFML,因此每次从事件循环生成事件时,都会将其发送到键盘类。然后,该类将该事件存储到相应的键中。

此外,我有class SynthesizerKey继承自KeyboardKey。除了常用键之外,例如“是按键启用”,“是按键”,此类包含处理生成正弦波音调的数据和函数。变量包括正弦波的幅度和电流相位。

我现在要创建一个class SynthesizerKeyboard。我准备将class Keyboard中的所有代码复制并粘贴到此类中,但这不是一个好的编程习惯,因为代码是重复的!

我遇到的主要问题是SynthesizerKeyboard包含一个生成要存储在缓冲区中的样本序列的函数。为了生成样本,循环遍历每个KeyboardKey并检查它是否被按下。如果是,那么我们必须生成与该键音符/频率相对应的样本。

然而,由于矢量包含class KeyboardKey class SynthesizerKey,我没有sin波的相位和幅度的变量作为元素的成员数据矢量。

我想我可能不得不做所谓的“重构”[?],并将SynthesizerKey的“{wave}”部分与KeyboardKey部分分开。换句话说,我放弃了SynthesizerKey类,并分别拥有Synthesizer类和KeyboardKey类。然后我在Synthesizer中添加了class SynthesizerKeyboard的向量,以及KeyboardKeyclass Keyboard的向量,我可以通过继承访问SynthesizerKeyboard

然而这不太优雅。还有另一种方式吗?

以下是一些可能有助于读者更详细地理解问题的代码。

SynthesizerKeyboard

class SynthesizerKeyboard : public Keyboard
{

public:

    SynthesizerKeyboard(const sf::Font& sf_font)
        : Keyboard(sf_font)
    {
    }

    double Sample() const
    {
        for(std::vector<KeyboardKey>::iterator it = m_keyboardkey.begin()
            it != m_keyboardkey.end(); ++ it)
        {
            if(it->IsKeyPressed())
            {
                it->Sample();
            }
        }
    }

    void GenerateBufferSamples(std::vector<sf::Int16> buffer)
    {
        for(std::size_t i = 0; i < buffer.size(); ++ i)
        {
            buffer[i] = Sample();
        }
    }

};

SynthesizerKey

class SynthesizerKey : public KeyboardKey
{

protected:

    AbstractOscillator *m_abstractoscillator;

public:

    double Sample() const
    {
        return m_abstractoscillator->Sample();
    }

};

键盘

class Keyboard
{

protected:

    std::vector<KeyboardKey> m_keyboardkey;

public:

    Keyboard(const sf::Font& sf_font)

    void Draw(sf::RenderWindow& window)

    void Event(const sf::Event& event)
    {
        for(std::vector<KeyboardKey>::iterator it = m_keyboardkey.begin();
            it != m_keyboardkey.end(); ++ it)
        {
            (*it).Event(event);
        }
    }

    bool IsKeyPressed(const sf::Keyboard::Key& sf_key)
    {
        for(std::vector<KeyboardKey>::iterator it = m_keyboardkey.begin();
            it != m_keyboardkey.end(); ++ it)
        {
            if((*it).Key() == sf_key)
            {
                return (*it).IsKeyPressed();
            }
        }
    }

};

KeyboardKey

class KeyboardKey
{

protected:

    KeyState m_keystate;
    sf::Color m_pressed_color;
    sf::Color m_release_color;
    sf::Text m_sf_text;
    sf::Keyboard::Key m_sf_keyboard_key;
    sf::RectangleShape m_sf_rectangle;

public:

    KeyboardKey(const sf::Keyboard::Key& sf_keyboard_key, const std::string& text, const sf::Font& sf_font,
                const double position_x, const double position_y)

    void Draw(sf::RenderWindow& window)

    void Event(const sf::Event& event)

    bool IsKeyPressed()

};

1 个答案:

答案 0 :(得分:0)

暂时跳过这些键,但你应该考虑类似的东西。

首先定义一个执行所有常见任务的裸骨摘要,并包含专门用于填充以执行其特定行为的类的挂钩:

class AbstractKeyboard
{
protected:
    std::vector<std::unique_ptr<KeyboardKey>> m_keyboardkey;
    void Draw();
    void Event()
    {
        for(auto &key: m_keyboardkey)
        {
            key->Event();
        }
    }

    bool IsKeyPressed(const sf::Keyboard::Key& sf_key)
    {
        for(auto &key: m_keyboardkey)
        {
            if(key->isKey(sf_key))
            {
                return key->IsKeyPressed();
            }
        }
        return false; // need this to handle the no match case, otherwise undefined behaviour
    }
    void doStuff()
    {
        // generic keyboard stuff goes here
        doSpecificStuff();
    }
    virtual void doSpecificStuff() = 0;
public:
    AbstractKeyboard(const sf::Font& sf_font);
    virtual ~AbstractKeyboard();
};

所有键盘都有键,所以键的向量就在这里。请注意,我们已经从vector个密钥转换为vector智能指针到密钥。现在我们可以使用任何继承基本键的键,例如合成器键,智能指针消除了处理指针的常见内存困境。

这里最重要的是doStuff功能。它做的东西。什么由你决定。当完成所有键盘必须执行的基本操作时,它会调用doSpecificStuff,这是每个继承者必须填写的函数,即使它什么也不做。 doSpecificStuff做任何继承者不同的做法,添加额外的行为,并且通常使合成器不仅仅是常规键盘。

这是基本的键盘:

class Keyboard:public AbstractKeyboard
{
protected:
    void doSpecificStuff()
    {
        // Do keyboard Stuff, if there is any specific stuff to do
    }
public:
    Keyboard(const sf::Font& sf_font): AbstractKeyboard(sf_font)
    {

    }

};

它没有做任何特别的事情,但可以将特殊代码放入doSpecificStuff

合成器为那些知道它是合成器(SampleGenerateBufferSamples)的民众添加了一些功能,并实现了doSpecificStuff来做合成器的东西。

class SynthesizerKeyboard : public AbstractKeyboard
{
protected:
    void doSpecificStuff()
    {
        // I do specific stuff! Yay me!
    }

public:

    SynthesizerKeyboard(const sf::Font& sf_font): AbstractKeyboard(sf_font)
    {

    }

    // leaving these as an example of how to get a SynthesizerKey out of m_keyboardkey
    double Sample() const
    {
        // just going to sum because I don't know what's supposed to happen in here
        double sum = 0.0;
        for(auto &key: m_keyboardkey)
        {
            if(key->IsKeyPressed())
            {
                if(SynthesizerKey* skey = dynamic_cast<SynthesizerKey*>(key.get()))
                {
                    sum += skey->Sample();
                }
                else
                {
                    // like freak out, man.
                }
            }
        }
        return sum;
    }

    void GenerateBufferSamples(std::vector<sf::Int16> buffer)
    {
        for(sf::Int16 & val: buffer)
        {
            val = Sample();
        }
    }

};

由于合成器使用合成器键,Sample包含如何将指向常规键的指针转换为合成器键并捕获将错误类型的键放入m_keyboardkey的配置错误的示例

通过在Sample中向SynthesizerKeyboard添加虚拟析构函数和虚拟关键字,我们也可以制作

class MoogSynthesizer: public SynthesizerKeyboard
{
public:
    double Sample() const override
    {
        // I do Moog sampling!            
    }
}