类包含指向基类的指针,但我不希望它。(C++)

Class contains pointer to base class but I don't want it to. (C++)

本文关键字:希望 C++ 包含指 基类 指针      更新时间:2023-10-16

一个奇怪的问题标题,但我不知道该如何紧凑地描述情况。

最近,我一直在问有关制作音频合成软件的问题。这个问题是关于特定问题的问题。我有。我的代码编译和工作正常;它只是"有点奇怪"如果愿意的话。

我有一个包含指向基类的指针。我将详细说明一个最小的示例,以详细说明发生了什么。

class SynthNoteBase
{
    // example content
    virtual
    double Sample() = 0;
}; // just for base class pointer
class SynthNoteA : public SynthNoteBase
{
};
class SynthNoteB : public SynthNoteBase
{
}; // define two different types of note -
   // these will synthesize different sounds

每个类SynthNote*都有一个成员,例如Play()Sample(),它将样本返回"播放"//quot"在音频文件中记录为"/&quot"。

现在,我有一个Synthesizer对象(类),其中包含许多SynthesizerKey对象。每个SynthesizerKey类都包含一个指向基类SynthNoteBase的指针。

说明:"Synthesizer&quot就像真正的合成器一样,有许多不同的Key S。这些Key对象将具有Play()方法或类似方法。主程序可以从输入源(键盘/文件)读取数据,并确定要播放的Key s。每个键可能想要不同的声音。(至少,一个人将需要不同的音符频率。该频率将是SynthNote*内的参数。)

到目前为止,一切都很好,除非构造SynthesizerKey对象。

class SynthesizerKey
{
protected:
    SynthNoteBase *m_synthnotebase_ptr;
public:
    SynthesizerKey(SynthNoteBase* const synthnote)
        : m_synthnotebase_ptr{synthnote}
    {
        
    }
    virtual
    ~SynthesizerKey()
    {
        delete m_synthnotebase_ptr; // could be a problem
    }
    void Play()
    {
        m_synthnotebase_ptr->Play(); // or whatever
    }
}

"问题"调用构造函数时发生。

SynthesizerKey key1(new SynthNoteA(...parameters...)); // had to use new here
// but we never delete! (delete hidden in destructor)

我感谢这并不是"问题"。per-se,但它确实产生了一些非常可怕的代码,其中最终用户正在用构造函数调用中的新内存分配内存,但似乎从未删除上述内存。

我的问题很清楚吗?这种情况有什么选择?

也许可以使用智能指针来解决这?我怀疑这确实是一个部分分辨率,因为我们可以使用智能指针隐藏"新"。部分以及"删除"部分...实际上不确定更好。

这是什么想法?

编辑1

(响应以下答案。)

我尝试了此解决方案:

class SynthesizerKey
{
    std::unique_ptr<SynthNoteBase> m_note;
    SynthesizerKey(const SynthNoteBase& note)
        : m_note(new ...) // ah: can't do this!
        // don't know what type to allocate with new?
    {
    }
}

但我不能分配内存,因为我将为基类类型分配存储而不是派生类型。

多态性的多态性工作您真的别无选择,只能使用指针。

至少对 hide 的可能的"解决方案"可能是将对基本对象作为参数作为参数的常数引用到SynthesizerKey构造函数,并处理SynthesizerKey构造函数初始化器列表中的内存分配和依靠"虚拟构造函数"(或克隆)模式复制对象。

另外,我建议您使用std::unique_ptr而不是使用" RAW"指针。那么至少您不必担心删除对象。


在您的情况下,"虚拟构造函数"(或克隆)模式将是这样的:

class SynthNoteBase
{
    // example content
    virtual
    double Sample() = 0;
    virtual SynthNoteBase* clone() = 0;
};
class SynthNoteA : public SynthNoteBase
{
    SynthNoteBase* clone()
    {
        return new SynthNoteA(*this);  // Allocates and invokes the copy-constructor
    }
};
class SynthNoteB : public SynthNoteBase
{
    SynthNoteBase* clone()
    {
        return new SynthNoteB(*this);  // Allocates and invokes the copy-constructor
    }
};
class SynthesizerKey
{
    std::unique_ptr<SynthNoteBase> m_note;
    SynthesizerKey(const SynthNoteBase& note)
        : m_note(note.clone())  // Clone the note, invokes copy-constructor correctly
    {
    }
};

如果这样做,请确保将Base class'destructor Virtual也要!
如果您通过Base *删除,则要确保派生的破坏者首先称为!

您对您要输入的内容的长度/措辞的"非常糟糕"的评论?如果是这样,我看不到您将如何解决:您需要所需的东西。

如果这是一种风格上的东西("有newdelete在哪里?"),请为这样做的知识感到安慰:

Minder(new Child());

意味着您显然"投降" Minder的指针来照顾 - 现在是它的问题!


您基本上有三个选项:整个对象,参考对象和指向对象。

  1. 全目标:将对对象的引用传递到构造函数中,拿一个本地副本,然后将传递的副本破坏。

    这里的问题是您不知道传递对象的类型,只是它是Base,因此任何Base复制都将是原始的"切片" - 失去虚拟度。另外:纯种方法可防止整个Base对象。

  2. 参考对象:如果仅记得参考,那么您就放弃了管理上述对象的内存的选项。

    you 可以做一个delete &reference;-但您最好还是保留了一个指针。

  3. 指针对象:这是您的解决方案,我看不到任何错误。

我不相信您应该使用smartptr-重点是什么?指针仅在一个地方;它是唯一可以访问的地方;破坏者将进入delete IT;解决问题。

使用 smartptr,而对象在对象的寿命中尚不清楚对象的所有权。它经过了很多。制作副本;那么该对象什么时候最终完成?在这里,物体的寿命是完全众所周知的,并在创建时间拥有一份所有权。不要使它复杂化。

虚拟构造函数是一个解决方案 - 以每个派生类中的 Clone()方法为代价。您仍然需要构造派生的对象来传递它 - 让其他人只采用 Clone()才能立即破坏它似乎是一项艰巨的努力。

您可以将注释成员表示为std :: unique_ptr,并通过值或引用synthesizerkey的构造函数(

)传递注释(从synthnotebase)传递。
#include <memory>
#include <iostream>
class SynthNoteBase
{
    public:
    virtual ~SynthNoteBase() {}
    virtual void Play() const = 0;
};
class SynthNoteA : public SynthNoteBase
{
    public:
    void Play() const override { std::cout << "Play An"; };
};
class SynthesizerKey
{
protected:
    std::unique_ptr<SynthNoteBase> m_synthnotebase_ptr;
public:
    template <typename Note>
    SynthesizerKey(Note&& note)
    :   m_synthnotebase_ptr{
            std::make_unique<typename std::decay<Note>::type>(
                std::forward<Note>(note))}
    {}
    void Play() const { m_synthnotebase_ptr->Play();  }
};
int main()
{
    SynthesizerKey keyA{SynthNoteA()};
    keyA.Play();
    return 0;
}