【问题标题】:How to change the type/typedef of a class member dynamicly?如何动态更改类成员的类型/类型定义?
【发布时间】:2021-11-02 23:57:54
【问题描述】:

以下问题。我正在做一个更大的 c++ 项目。该项目有一个中心数据类:mal::FrameAudio,数据类如下所示:

class FrameAudio  {
 public:

  typedef Eigen::Matrix<unsigned char, Eigen::Dynamic, Eigen::Dynamic> pluginType;
...
const pluginType& getPlug() const;
void setPlug(const pluginType& plugin);
...

现在我想扩展软件的一些功能,在我的情况下我需要一个不同的pluginType

typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> pluginType;

如何在不破坏其余代码功能的情况下实现新的 dataType?

  1. 继承?我想过做这样的 FrameAudioFloat: public FrameAudio。但是数据存储在软件中更早地被初始化,超出了我的功能,据我所知,当指针设置为基类时,您只能使用派生类的覆盖函数。所以我不能调用 getPlug() 因为它有另一个返回类型。

  2. 模板?我在考虑创建一个模板类?但是我看到代码经常使用数据存储的 typedef。你会经常在代码中找到这样的东西:

    const mal::FrameAudio::pluginType&amp; plugins = aFrame.getPlug();

我什至在其他类的成员定义中发现了 typedef。那么,对我来说,用新的 typedef 和函数扩展这个类的最简单方法是什么?现在,如果我的功能适用于新的 typedef 并且其余代码仍然适用于旧的,这对我来说就足够了。也许使用 auto 作为 typedef?

【问题讨论】:

  • 请问起源问题是什么? (对于以下问题
  • 也许std::variant 是一个选项,虽然不是在 C++17 之前。

标签: c++ inheritance typedef


【解决方案1】:

您不能动态更改对象的类型,C++ 不允许这样做。前面说了这么多。

现在,使用模板可能是正确的方法。关键是,您可能无法在保持代码不变的情况下更改它,这正是您所要求的。相反,您可以编写类似的代码,但将unsigned char 替换为float,因为这不会触及现有代码。为了避免实际的代码重复,创建模板是可行的方法:

template <typename scalar> class FrameAudioT
{
    // type from the outer class used in the plugin type
    typedef Eigen::Matrix<scalar, Eigen::Dynamic, Eigen::Dynamic> pluginType;
    // ...other code...
};
// for compatibility with existing code:
typedef FrameAudioT<char> FrameAudio;

FrameAudio 类开始。然后,选择下一个仍然依赖于遗留的非模板FrameAudio 类型的类。一旦不再使用此类型,您可以删除 typedef。

当然,在中间你可以重构代码。例如,如果你发现你的代码只使用了插件接口的一个子集,它不依赖于它的第一个参数,你可以创建一个纯虚拟基类(“接口类”)并使用它。细节取决于您的应用程序,因此很难给出明确的建议。

【讨论】:

  • 哇 这真是个好主意,尤其是使用 typedef !所以现在例如,如果我有另一个将 FrameAudio 作为类成员的 DataStorage 类,我基本上也会这样做:DataStorageT .. 当 typedef 被这样调用时:'const mal::FrameAudio::pluginType' 我需要将其更改为 const mal::FrameAudio::pluginType 用于新案例?
  • 最后一个问题。我想这就是你最后的意思。有一些函数独立于这个 typedef 和它相关的问题。像 void do(const mal::FrameAudio& frame),我需要在这里改变什么吗? FrameAudio 和 FrameAudio 是否仍被识别为函数的 FrameAudio?
  • 不,这行不通。如果上述任何“...其他代码...”不依赖(甚至间接)标量类型,您可以将其移至非模板基类,从中派生上述模板类。
  • 这就是我开始做的事情,我认为这是一个非常好的主意!现在在完成我的想法和计划之后。我想知道为什么模板类与派生两个类相比。我认为 typedef FrameAudio 技巧仍然适用于派生类。模板的方式是不是和继承基本一样,唯一的不同就是只能保持相同的功能吗?而且变化也是一样的,对于模板我必须为类 FrameAudioFloat 声明 FrameAudio。那么模板的好处是我不用复制那么多代码呢?
  • “模板的优点是我不必复制那么多代码”——就是这样。您避免使用几乎相同的代码编写单独的类。
【解决方案2】:

我会选择模板。通过将插件类型默认为现有类型,您可以将现有代码转换为基本案例:

template <class PluginType = 
    Eigen::Matrix<unsigned char, Eigen::Dynamic, Eigen::Dynamic
>
class FrameAudio  {
 public:

  typedef PluginType pluginType;
...
const pluginType& getPlug() const;
void setPlug(const pluginType& plugin);
};

因此,当使用 FrameAudio 而不指定模板参数时,它的行为与以前一样,这意味着现有代码和 typedef 都可以工作。此外,您还为新插件类型提供了专业化:

template <>
class FrameAudio<
    Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic>
>
{
public:
    using PluginType = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic>;
    // You can even access classes of the base template 
    // if you want to share implementations
    ....
};

另一个巧妙的技巧是交换幕后的类型,例如定义

using FrameAudio = FrameAudioG<
    Eigen::Matrix<unsigned char, Eigen::Dynamic, Eigen::Dynamic>
>;

其中FrameAudioG 是具有模板化插件类型的通用版本(如上所示)。通过这种方式,新用户可以使用通用接口,这两个插件都可以访问,并且旧代码可以像以前一样继续工作。

【讨论】:

  • 谢谢!我以前从未见过这样使用过的模板。在第一个代码示例中,您正在创建一个具有默认值的模板。如果您只调用 FrameAudio,这是否有效,或者我需要将其更改为 FrameAudio。请原谅我缺少知识,但第二部分发生了什么?使用 PluginType 在那里做什么?
  • @Oroshimaru 是的,除了一种情况外,它无需指定模板参数即可工作;唯一需要&lt;&gt; 的情况是使用嵌套的typedef (FrameAudio&lt;&gt;::PluginType)。在这种情况下,using 只是 typedef 的另一种语法,它更易于阅读,因为新别名位于赋值运算符的左侧
  • @LorahAttkins 添加到您的答案中我认为您在声明指针时还需要 对吗?见:stackoverflow.com/a/46085772/16743536
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-11
  • 1970-01-01
  • 1970-01-01
  • 2011-09-24
  • 2014-07-26
  • 2016-09-19
  • 1970-01-01
相关资源
最近更新 更多