继承和 2 种接口类型
Inheritance and 2 interface types
我有一个关于继承和设计用户界面的问题。
我有一个class KeyboardKey
,它代表单个键盘键,例如Q,W,E,R,...等。
我有一个包含class Keyboardkey
向量的class Keyboard
。[重要!
我正在使用 SFML,因此每次从事件循环生成事件时,它都会发送到键盘类。然后,此类将该事件农场化到相应的键。
此外,我还有一个继承自KeyboardKey
class SynthesizerKey
。除了常规键的内容,例如"是否启用键","是否按下键",此类还包含用于处理生成正弦波音调的数据和函数。变量包括正弦波的振幅和当前相位。
我现在要创建一个class SynthesizerKeyboard
.我正要从class Keyboard
复制并粘贴所有代码到这个类中,但是这不是好的编程实践,因为代码是重复的!
我遇到的主要问题是SynthesizerKeyboard
包含一个函数来生成要存储在缓冲区中的样本序列。为了生成样本,循环遍历每个KeyboardKey
并检查它是否被按下。如果是,那么我们必须生成一个对应于该键音符/频率的样本。
但是,由于向量包含class KeyboardKey
而不是class SynthesizerKey
因此我没有正弦波的相位和振幅的变量作为向量元素的成员数据。
我想我可能不得不做所谓的"重构"[?],并将SynthesizerKey
的"罪恶波"部分与KeyboardKey
部分分开。换句话说,我放弃了SynthesizerKey
类,分别有一个Synthesizer
类和一个KeyboardKey
类。然后,除了class Keyboard
中KeyboardKey
的向量之外,我还class SynthesizerKeyboard
Synthesizer
向量,我可以通过继承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();
}
}
};
合成器键
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();
}
}
}
};
键盘键
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()
};
现在要跳过键,但你应该考虑类似的东西。
首先定义一个执行所有常见任务的裸骨抽象,并包括用于专门填充抽象以执行其特定行为的类的钩子:
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
.
合成器为知道它是合成器(Sample
和GenerateBufferSamples
)的人添加了一些功能,并实现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
的配置错误
通过在SynthesizerKeyboard
中添加虚拟析构函数和 virtual 关键字来Sample
,我们还可以制作一个
class MoogSynthesizer: public SynthesizerKeyboard
{
public:
double Sample() const override
{
// I do Moog sampling!
}
}
- 无法使用接口类型对priority_queue中的对象进行排序<T>
- 如何正确存储/传递接口类型的变量(现代 c++)?
- C :可以从类及其受保护的成员类型继承可以继承吗?
- C++ 接口类继承自具体类是否合理
- C++接口、继承、多态性
- 模板类上的类型继承
- C 类从类型继承
- 如何根据接口类型运行
- C 将功能实现对象作为接口类型
- 继承和 2 种接口类型
- 如何使用Qt在Windows上检查网络接口类型是以太网或无线
- 在 C++11 中,从私有嵌套类型继承是否合法?
- 模板基类型继承
- 在 C# 中加载 COM 对象会引发异常"无法将类型'System.__ComObject'的 COM 对象强制转换为接口类型...",但C++或 VB 不会
- C#中从指定类型继承的模板类的等效项是什么
- 为boost mpl列表中的每种类型继承容器
- 从基类和返回类型继承模板
- 从基元类型继承的枚举
- 使用类型继承的数字组的层次结构
- 使用接口类型查找子窗口小部件