3.多媒体(音频、视频)
播放声音需要引入multimedia模块
使用QSound
类
仅支持的音频文件格式为.wav
,同样使用qrc
文件管理外部的资源
(使用的.wav
文件不宜过大,尽量在几秒内,否则会构建时间过长)
wav是一种无损格式,能够提供比其他格式更高质量的音频,wav文件的质量比较高,从而文件大小也相对会大一点
1>用按钮控制播放音频
在.pro
中导入multimedia
模块:
#include "widget.h"
#include "ui_widget.h"
#include <QSound>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
//1.直接调用 QSound::play
//QSound::play(":/eatfood.wav");
//2.创建 QSound 实例并播放
QSound* sound = new QSound(":/eatfood.wav",this);
sound->play();
//delete sound;
//这里不能加delete,原因在下方说明
}
1. 直接调用QSound::play
会不会导致内存泄漏
不会,QSound::play
是一个静态成员函数,直接调用它不会创建新的对象,因此也不需要担心内存释放的问题。由于它是静态成员函数,它是由类本身调用的,而不是某个对象,因此不涉及对象的内存管理问题。这个方法适用于播放短小的声音文件。
2. 为什么不能加delete,为什么加了delete后就播放不出声音了
实际上播放了,但是在听到音频之前就执行了delete,立即删除 QSound
对象时,声音播放的过程会被打断,因为 QSound
对象在播放声音的过程中需要存在。删除对象会终止声音播放,所以你会听不到声音。
3. 那不能加delete,这种手动创建的方式会导致内存泄漏吗
有可能, 如果你不删除 QSound
对象,确实可能会导致内存泄漏。每次点击按钮都会创建一个新的 QSound
对象,如果不释放这些对象,内存会不断增长,最终可能会耗尽内存。
4. 针对第三问如何解决
使用事件循环解决内存泄漏问题,第六点的第④种方法
可以使用 QObject
的事件机制来在声音播放结束时自动删除 QSound
对象。具体可以通过连接 QSound
的 finished
信号到一个槽函数,在槽函数中删除 QSound
对象。
示例代码如下:
void Widget::on_pushButton_clicked()
{
QSound* sound = new QSound(":/eatfood.wav", this);
connect(sound, &QSound::finished, sound, &QObject::deleteLater);
sound->play();
}
//deleteLater 方法会在事件循环的下一个空闲时刻自动删除 sound 对象。
//这种方法可以确保声音播放完成后再删除对象,从而避免内存泄漏和播放被中断的问题。
5. 事件循环的原理
- 事件循环 (
event loop
) 是 GUI 应用程序的核心机制之一,用于管理和分发事件。 - Qt 的事件循环(
QCoreApplication::exec
或QApplication::exec
)主要负责处理用户输入、定时器、网络事件等。它不直接管理QSound
对象的释放。 - Qt 的事件循环是通过
QCoreApplication
或其子类(如QApplication
)实现的。 - 当一个事件(如信号发射)发生时,事件循环会将事件放入事件队列中,并在适当的时候处理这些事件。
deleteLater
方法将删除操作推迟到事件循环的下一次空闲时刻,这样可以确保对象在安全的时刻被删除。
6. 其他解决办法
① 使用智能指针(如 std::unique_ptr
或 std::shared_ptr
)
智能指针可以自动管理对象的生命周期,当智能指针超出作用域时,它会自动删除所指向的对象。这种方法简单,但需要确保对象在播放完成之前不会超出作用域。
② 手动管理对象的生命周期
你可以创建一个成员函数或使用 Qt 的定时器来延迟删除 QSound
对象。
③ 使用 QTimer
延迟删除
通过 QTimer
设置一个定时器,在声音播放结束后删除 QSound
对象。可以根据声音文件的长度调整定时器的时间。
④ 使用 QEventLoop
在需要的情况下,你可以手动创建一个 QEventLoop
来等待声音播放完成。QEventLoop
在声音播放完成前一直运行,确保 QSound
对象在播放完成后才销毁。
⑤ 将 QSound
作为成员变量
如果你希望反复使用同一个 QSound
对象,可以将它作为类的成员变量。这种方法避免了反复创建和删除对象,但需要注意对象的重用。通过将 QSound
作为成员变量,你只需创建一次对象,并在需要时播放声音。
2>使用第六种解决方法解决上题
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QSound>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_pushButton_clicked();
private:
Ui::Widget *ui;
QSound* sound;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
sound = new QSound(":/eatfood.wav",this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
sound->play();
}
- 对象持久化: 通过将
QSound
对象作为类的成员变量,可以确保对象在整个类的生命周期内都存在,不会在每次使用时创建和销毁。这减少了对象的创建和销毁开销。 - 内存管理: 通过在构造函数中创建对象,并在析构函数中删除对象,可以确保在对象不再需要时正确释放内存,从而避免内存泄漏。
- 父子对象机制: 在创建
QSound
对象时,我们将Widget
对象设为其父对象。这意味着当Widget
对象销毁时,Qt 会自动销毁其所有子对象,包括QSound
对象。这进一步简化了内存管理。
优缺点
- 优点:
①避免了频繁创建和销毁对象的开销。
②简化了内存管理,减少了内存泄漏的风险。
③使用父子对象机制,使对象的生命周期与父对象同步。
- 缺点:
①如果需要播放不同的声音文件,必须重新创建或重新配置 QSound
对象。
②如果 Widget
对象生命周期较长,而声音播放不频繁,可能会占用一些不必要的内存。