1

在 JNI .cpp 文件中,我有一个带有 SoundTouch* 的结构(SoundTouch 是一个 C++ 音频处理,我将其包装以在 Android 项目中使用),并且我将结构的向量初始化为全局对象,如下所示:

struct SoundTouchExt
{
    SoundTouch* sTouch;
    queue<signed char>* fBufferOut;
    int channels;
    int sampleRate;
    float tempoChange;
    int pitchSemi;
    int bytesPerSample;

    SoundTouchExt()
    {
        sTouch = new SoundTouch();
        fBufferOut = new queue<signed char>();
    }
};

const int MAX_TRACKS = 16;

vector<SoundTouchExt> sProcessors(MAX_TRACKS);

这是可行的,至少如果我在我的程序中一次只使用一个 SoundTouchExt 对象(这是一个不同的故事,但可能相关 - 播放多个实例会导致输出失真)。

但是,如果我这样声明它SoundTouch sTouch;,注释掉new并相应地更改它的使用(->.),指向引用的指针,我编译得很好,但是一旦程序尝试使用该对象,我就会得到一个 FAULT 11(段错误) .

这是发生这种情况的地方:

...
    SoundTouchExt& soundTouch = sProcessors.at(track);
    setup(soundTouch, channels, samplingRate, bytesPerSample, tempo, pitchSemi);
}

static void setup(SoundTouchExt& soundTouch, int channels, int sampleRate, int bytesPerSample, float tempoChange, float pitchSemi)
{
    SoundTouch& sTouch = soundTouch.sTouch;

    soundTouch.channels = channels;
    soundTouch.sampleRate = sampleRate;
    soundTouch.bytesPerSample = bytesPerSample;
    soundTouch.tempoChange = tempoChange;
    soundTouch.pitchSemi = pitchSemi;

    sTouch.setSampleRate(sampleRate);
    sTouch.setChannels(channels);
...
}

通过一些研究,我认为这可能是静态初始化命令 fiasco的一个实例。我在库源代码中看不到任何全局变量,但我对 C++ 了解得不够多,不知道还要寻找什么。

我的观察对图书馆有什么建议(或者我可能没有正确地做某事)?

4

1 回答 1

4

我相信您的SoundTouch结构/类在其复制构造函数和/或赋值运算符中存在问题。或者你甚至没有写那些但需要。

我什至看不到 SoundTouch 的代码,为什么要这样说?出色地...

SoundTouchExt在如何管理其 sTouch 成员(以及 fBufferOut )方面存在问题。每个实例都会创建自己的 sTouch,但是当您的对象被复制时,您没有复制构造函数或赋值运算符来处理 sTouch 成员。编译器提供的默认值将简单地执行浅成员明智的复制。因此,如果一个 SoundTouchExt 对象被分配给另一个对象,那么它们最终都会得到指向同一个 SoundTouch 的 sTouch 指针。我怀疑你曾经打算让这种情况发生。但是,由于您也没有析构函数来清理这些分配,因此您可能会暂时摆脱这种情况(因为内存泄漏很容易被忽略)。

看起来你确实在使用vector<SoundTouchExt>. 向量管理一个内部数组。当您将条目添加到向量时,它有时可能会用完当前数组中的空间,因此需要创建一个新数组来保存额外的条目。这样做时,它必须将旧数组中的所有条目复制到新数组中。这样就使用了 SoundTouchExt 的复制构造函数和/或赋值运算符。您不会注意到这一点,因为使用相同 SoundTouch 的两个 SoundTouchExt 实例的情况仅在旧数组中的一个实例被销毁之前短暂存在。而且因为 SoundTouchExt 缺少析构函数,所以没有什么会导致问题。

现在考虑当 sTouch 成员是实际的 SoundTouch 实例而不是指针时情况如何变化。在这种情况下,当复制 SoundTouchExt 对象并因此复制 sTouch 成员时,这意味着编译器将使用 SoundTouch 复制构造函数/赋值运算符。我们知道您的载体会导致这种情况发生。

由于您的 SoundTouchExt 存在我所描述的复制问题,我怀疑您的 SoundTouch 也存在问题。如果是这种情况,那么当您开始尝试使用 sTouch 成员时,它可能已经被复制并因此导致了某种问题。然后,该问题会导致您的崩溃。

因此,要解决问题,您有几个选择:

  • 确保所有相关对象在被复制时都正确运行。这可能意味着实现复制构造函数和赋值运算符。在这种情况下,您很可能应该按照三规则实现析构函数。
  • 或者禁用它们的复制构造函数和赋值运算符(将它们声明为私有但不实现),这样它们就不会被意外使用。那可能需要一些其他的重构,比如你的向量使用。尽管如果您有可用的 C++11 功能,您也许可以使用移动运算符/构造函数,以便您的对象可以像现在一样在标准容器中工作,但在适当的时候正确地转移它们的成员。无论哪种方式,析构函数都可能仍然是必需的。
  • 或者用某些类型的智能指针(如 unique_ptr)替换这些原始指针,它可以自动管理分配的任何内容,new而无需编写任何额外的代码。
于 2014-01-15T05:17:09.363 回答