SDL2 - Visual Studio 2017 SDL_FreeWAV 访问冲突
SDL2 - Visual Studio 2017 SDL_FreeWAV Access Violation
我正在使用 SDL2,并创建了一个 Audio
class 来处理游戏音乐和音效。声音效果很好,但是每当 Audio
class 被破坏时, SDL_FreeWAV()
调用就会引发访问冲突:
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);
}
};
只要您有默认构造函数并且没有定义自己的构造函数,C++ 编译器就会免费为您提供 move constructor
和 copy constructor
。他们浅拷贝对象的所有成员。
当你使用operator=
(对象赋值给另一个对象)时,它使用copy
构造函数。当你 return 临时对象(右值)时,它使用 move
构造函数。
遗憾的是,默认的复制和移动构造函数不适合您。
考虑一下:
Audio a = Audio("filename", "channel");
在这行看似简单的代码中,您是:
- 创建临时(右值)
Audio
对象
- 呼叫
Audio::operator=
- 使用
move constructor
- 正在删除临时对象
所以在这行有效的 C++ 之后,a
有:
device_id
已关闭。
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
被取消,因此它在被销毁时不会被清除。
我正在使用 SDL2,并创建了一个 Audio
class 来处理游戏音乐和音效。声音效果很好,但是每当 Audio
class 被破坏时, SDL_FreeWAV()
调用就会引发访问冲突:
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);
}
};
只要您有默认构造函数并且没有定义自己的构造函数,C++ 编译器就会免费为您提供 move constructor
和 copy constructor
。他们浅拷贝对象的所有成员。
当你使用operator=
(对象赋值给另一个对象)时,它使用copy
构造函数。当你 return 临时对象(右值)时,它使用 move
构造函数。
遗憾的是,默认的复制和移动构造函数不适合您。
考虑一下:
Audio a = Audio("filename", "channel");
在这行看似简单的代码中,您是:
- 创建临时(右值)
Audio
对象 - 呼叫
Audio::operator=
- 使用
move constructor
- 正在删除临时对象
所以在这行有效的 C++ 之后,a
有:
device_id
已关闭。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
被取消,因此它在被销毁时不会被清除。