c++ - 将全局对象初始化为指针是唯一成功的方法

标签 c++ android-ndk java-native-interface soundtouch

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

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);
...
}

通过一些研究,我认为这可能是 static intialization order fiasco 的一个实例.我在库源代码中没有看到任何全局变量,但我对 C++ 的了解还不够,不知道还需要寻找什么。

我的观察对图书馆有什么建议(或者我可能做错了什么)?

最佳答案

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

当我什至看不到 SoundTouch 的代码时为什么要说这个?嗯……

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

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

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

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

所以要解决问题,您有几个选择:

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

关于c++ - 将全局对象初始化为指针是唯一成功的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21127935/

相关文章:

android - 无法找到头文件 - Android NDK

c++ - winapi - Hook 应用程序的任务栏选项卡事件

c++ - 我如何使用 §8.3.1 和 §8.3.4 推断像 '[]' 这样的后缀声明符运算符比像 '*' 这样的前缀运算符绑定(bind)得更紧密?

C++ - 将数组传递给方法

java - 如何让 JNI 在调用 native 函数时重新加载共享库?

c - 如何使用 JNI 打印宽字符

android - 在 android ndk 中定义 sem_t

c++ - union 是否始终具有默认值零?

c++ - 安卓ndk camera2 api

c++ - Android NDK cmake 和依赖库