派生 classes 具有不同输入结构参数的 C++ 基础 class 方法
C++ base class method with different input struct argument for derived classes
我正在尝试创建一个基础 class 定义派生 classes 的接口 - 比如说一个简单的音频包装器 class。
class AudioStreamBase
{
public:
virtual
~AudioStreamBase(void);
virtual void
open(const void * settings) = 0;
virtual void
start(void) = 0;
virtual void
stop(bool force) = 0;
virtual void
close(void) = 0;
virtual int
recover(int err) = 0;
virtual int
readFrames(void * buffer) = 0;
virtual int
writeFrames(void * buffer) = 0;
virtual void
printConfig(void) = 0;
};
由于在Linux/Win/embedded系统中不同的实现可能接受不同的配置参数,我将open()的输入参数定义为:
const void * 设置
一种可能的实现方式是:
struct settingsA
{
int param1;
int param2;
...
};
class AudioStreamA : public AudioStreamBase
{
public:
...
void open(const void* settings) { settingsA * s = (settingsA) settings; ...};
...
}
但是,这似乎不是很 C++ - 我已经习惯了 C 很多,几年后我再次开始使用 C++。有更好的解决方案吗?我正在考虑对 class 或方法进行模板化,但由于类型是结构,我将无法访问参数。并且构建一个复杂的结构构造来读取它的参数对于像访问结构中的几个参数这样简单的事情来说似乎很模糊。
您可以使用 CRTP 将派生的 class 注入到基础中。然后你可以定义一个特征助手来定义例如哪些设置必须用于某些派生 class。诚然,有点冗长,但现在您的代码是 type-safe.
如果您需要底数 class 为 non-template,您可以将除 open
之外的所有内容都拉出到 non-template 顶底数 class 中。
class AudioStreamA;
struct settingsA;
template<class T>
struct AudioStreamTraits;
template<>
struct AudioStreamTraits<AudioStreamA> {
using Settings = settingsA;
};
template<class Derived>
class AudioStreamBase
{
protected:
using StreamTraits = AudioStreamTraits<Derived>;
using Settings = typename StreamTraits::Settings;
public:
virtual
~AudioStreamBase() = default;
virtual void
open(const Settings& settings) = 0;
//...
};
struct settingsA
{
int param1;
int param2;
};
class AudioStreamA : public AudioStreamBase<AudioStreamA>
{
using Base = AudioStreamBase<AudioStreamA>;
using Base::Settings;
public:
void open(const Settings& settings) override { };
};
int main() {
AudioStreamA s;
settingsA settings;
s.open(settings);
return 0;
}
查看代码示例 here
但是,如果您只需要派生 class 中的设置,您可以简单地将 open
替换为派生 class:
中的构造函数
class AudioStreamBase
{
public:
virtual
~AudioStreamBase() = default;
// ....
};
struct settingsA
{
int param1;
int param2;
};
class AudioStreamA : public AudioStreamBase
{
public:
AudioStreamA(const settingsA& settings) { } ;
};
我正在尝试创建一个基础 class 定义派生 classes 的接口 - 比如说一个简单的音频包装器 class。
class AudioStreamBase
{
public:
virtual
~AudioStreamBase(void);
virtual void
open(const void * settings) = 0;
virtual void
start(void) = 0;
virtual void
stop(bool force) = 0;
virtual void
close(void) = 0;
virtual int
recover(int err) = 0;
virtual int
readFrames(void * buffer) = 0;
virtual int
writeFrames(void * buffer) = 0;
virtual void
printConfig(void) = 0;
};
由于在Linux/Win/embedded系统中不同的实现可能接受不同的配置参数,我将open()的输入参数定义为:
const void * 设置
一种可能的实现方式是:
struct settingsA
{
int param1;
int param2;
...
};
class AudioStreamA : public AudioStreamBase
{
public:
...
void open(const void* settings) { settingsA * s = (settingsA) settings; ...};
...
}
但是,这似乎不是很 C++ - 我已经习惯了 C 很多,几年后我再次开始使用 C++。有更好的解决方案吗?我正在考虑对 class 或方法进行模板化,但由于类型是结构,我将无法访问参数。并且构建一个复杂的结构构造来读取它的参数对于像访问结构中的几个参数这样简单的事情来说似乎很模糊。
您可以使用 CRTP 将派生的 class 注入到基础中。然后你可以定义一个特征助手来定义例如哪些设置必须用于某些派生 class。诚然,有点冗长,但现在您的代码是 type-safe.
如果您需要底数 class 为 non-template,您可以将除 open
之外的所有内容都拉出到 non-template 顶底数 class 中。
class AudioStreamA;
struct settingsA;
template<class T>
struct AudioStreamTraits;
template<>
struct AudioStreamTraits<AudioStreamA> {
using Settings = settingsA;
};
template<class Derived>
class AudioStreamBase
{
protected:
using StreamTraits = AudioStreamTraits<Derived>;
using Settings = typename StreamTraits::Settings;
public:
virtual
~AudioStreamBase() = default;
virtual void
open(const Settings& settings) = 0;
//...
};
struct settingsA
{
int param1;
int param2;
};
class AudioStreamA : public AudioStreamBase<AudioStreamA>
{
using Base = AudioStreamBase<AudioStreamA>;
using Base::Settings;
public:
void open(const Settings& settings) override { };
};
int main() {
AudioStreamA s;
settingsA settings;
s.open(settings);
return 0;
}
查看代码示例 here
但是,如果您只需要派生 class 中的设置,您可以简单地将 open
替换为派生 class:
class AudioStreamBase
{
public:
virtual
~AudioStreamBase() = default;
// ....
};
struct settingsA
{
int param1;
int param2;
};
class AudioStreamA : public AudioStreamBase
{
public:
AudioStreamA(const settingsA& settings) { } ;
};