【问题标题】:SDL2 - Visual Studio 2017 SDL_FreeWAV Access ViolationSDL2 - Visual Studio 2017 SDL_FreeWAV 访问冲突
【发布时间】:2018-09-18 19:33:17
【问题描述】:

我正在使用 SDL2,并创建了一个 Audio 类来处理游戏音乐和音效。声音完美无缺,但每当 Audio 类被破坏时,SDL_FreeWAV() 调用就会引发访问冲突:

Program.exe 中的 0x000000006C7A8737 (SDL2.dll) 处引发异常: 0xC0000005:访问冲突读取位置0x00007FF4A9080008

音频.h:

#pragma once

namespace Audio {

    class Audio {

    public:

        Audio ();
        Audio (char*, char*);
        ~Audio ();

        void pause (int);

    private:

        Uint32 wav_length;
        Uint8 *wav_buffer = NULL;
        SDL_AudioSpec wav_spec;
        SDL_AudioDeviceID device_id;
        int success;

    };

    class Music {

    public:

        Music ();
        Music (char*);
        ~Music ();

    private:

        Audio *audio = NULL;

    };

    class Effect {

    public:

        Effect ();
        Effect (char*);
        ~Effect ();

    private:



    };

};

音频.cpp

#include "stdafx.h"
#include "Audio.h"

#include "SDL_audio.h"

namespace Audio {

    Audio::Audio () {

        //Default constructor

    }

    Audio::Audio (char *filename, char *channelName) {

        if (SDL_LoadWAV (filename, &this->wav_spec, &this->wav_buffer, &this->wav_length) == NULL) {

            std::cout << "[-] SDL: " << SDL_GetError () << "\n";

            exit (ERROR_SDL_AUDIO_WAV_LOAD);

        }

        this->device_id = SDL_OpenAudioDevice (channelName, 0, &this->wav_spec, NULL, 0);

        this->success = SDL_QueueAudio (this->device_id, this->wav_buffer, this->wav_length);

    }

    Audio::~Audio () {

        SDL_CloseAudioDevice (this->device_id);
        SDL_FreeWAV (this->wav_buffer); // <-- access violation here

    }

    void Audio::pause (int on) {

        SDL_PauseAudioDevice (this->device_id, on);

    }

};

【问题讨论】:

  • 让我快速猜测一下,在某个地方制作了一个临时对象,它会在缓冲区死亡时关闭并释放您的缓冲区。如果你实现一个正确的复制(或移动)构造函数会发生什么?

标签: c++ visual-c++ access-violation


【解决方案1】:

只要您有一个默认构造函数并且没有定义自己的构造函数,C++ 编译器就会免费为您提供move constructorcopy constructor。他们浅拷贝对象的所有成员。

当您使用operator=(将对象分配给另一个对象)时,它使用copy 构造函数。当您返回一个临时对象(右值)时,它使用move 构造函数。

遗憾的是,在这种情况下,默认的复制和移动构造函数不适合您。

考虑一下:

Audio a = Audio("filename", "channel");

在这行看似简单的代码中,您是:

  1. 制作一个临时(右值)Audio 对象
  2. 致电Audio::operator=
  3. 使用move constructor
  4. 删除临时对象

所以在这行有效的 C++ 之后,a 有:

  1. device_id 已关闭。
  2. wav_buffer 已被释放。

那么我们该如何解决这个问题呢?

Audio::Audio (char *filename, char *channelName) {
    if (SDL_LoadWAV (filename, &this->wav_spec, &this->wav_buffer, &this->wav_length) == NULL) {
        std::cout << "[-] SDL: " << SDL_GetError () << "\n";
        exit (ERROR_SDL_AUDIO_WAV_LOAD);
    }
    this->device_id = SDL_OpenAudioDevice (channelName, 0, &this->wav_spec, NULL, 0);
    this->success = SDL_QueueAudio (this->device_id, this->wav_buffer, this->wav_length);
}

Audio::Audio(Audio&& other) : // move constructor
    wav_length(other.wav_length),
    wav_buffer(other.wav_buffer),
    wav_spec(other.wav_spec),
    device_id(other.device_id),
    success(other.success)
{
    other.wav_buffer = nullptr;
}

Audio::~Audio () {
    if(wav_buffer != nullptr) {
        SDL_CloseAudioDevice (this->device_id);
        SDL_FreeWAV (this->wav_buffer); // Gives access violation
    }
}

现在,当一个Audio 对象被移动时,它的wav_buffer 被无效化,这样它在被销毁时就不会被清理。

【讨论】:

  • 看起来问题实际上是解构器被调用了两次,因为它既在程序中被调用,又在关闭时被调用。虽然如果需要,我会记住复制构造函数。
猜你喜欢
  • 2021-06-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-09
  • 2015-09-23
  • 1970-01-01
相关资源
最近更新 更多