简体   繁体   中英

SDL2 - Visual Studio 2017 SDL_FreeWAV Access Violation

I'm using SDL2, and created an Audio class to handle game music and sound effects. The sound works perfectly, but whenever the Audio class is destructed, the SDL_FreeWAV() call throws an access violation:

Exception thrown at 0x000000006C7A8737 (SDL2.dll) in Program.exe: 0xC0000005: Access violation reading location 0x00007FF4A9080008

Audio.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:



    };

};

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

    }

};

As long as you have a default constructor and didn't define your own, the C++ compiler gives you a move constructor and a copy constructor for free. They shallow-copy all members of the object.

When you use operator= (assignment of the object to another object), it uses the copy constructor. When you return a temporary object (rvalue), it uses the move constructor.

Sadly the default copy and move constructors don't fit you in this case.

Consider this:

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

In this deceptively simple line of code you're:

  1. Making a temporary (rvalue) Audio object
  2. Calling Audio::operator=
  3. Using the move constructor
  4. Deleting the temporary object

So after this line of valid C++, a has:

  1. device_id that was closed.
  2. wav_buffer that was freed.

So how do we fix this?

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
    }
}

Now when an Audio object is moved, its wav_buffer is nullified so that it won't be cleaned up when it's destroyed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM