1背景
hplayer学习4 上一篇追踪完了工厂方法创建HFFPlayer,这一篇继续后续的代码
2 步骤
2.1 start后续
void HVideoWidget::start() {
if (media.type == MEDIA_TYPE_NONE) {
QMessageBox::information(this, tr("Info"), tr("Please first set media source, then start."));
updateUI();
return;
}
if (!pImpl_player) {
pImpl_player = HVideoPlayerFactory::create(media.type);
pImpl_player->set_media(media);
pImpl_player->set_event_callback(hplayer_event_callback, this);
title = media.src.c_str();
int ret = pImpl_player->start();
if (ret != 0) {
onOpenFailed();
}
else {
onOpenSucceed();
}
updateUI();
}
else {
if (status == PAUSE) {
resume();
}
}
}
工厂方法创建HFFPlayer后,传入了从之前选择的媒体文件信息(src,type等等)
2.2 set_media
pImpl_player->set_media(media);
追踪HFFPlayer(src/video/HFFPlayer.h)没看到有对应的方法,继续从基类(HVideoPlayer)找
class HFFPlayer : public HVideoPlayer, public HThread
基类的头文件中可以看到如下定义
class HVideoPlayer
{
public:
void set_media(HMedia& media) {
this->media = media;
}
public:
HMedia media;
成员函数在头文件实现了,也没啥问题,最多头文件冗余
继续之前start后续
2.3 set_event_callback
之前的pImpl_player,上一篇里有从头文件(src/ui/HVideoWidget.h)找到如下声明
class HVideoWidget : public QFrame
{
private:
HVideoPlayer* pImpl_player;
在HVideoPlayer的头文件(src/video/HVideoPlayer.h)中可以找到如下代码
class HVideoPlayer
{
public:
void set_event_callback(hplayer_event_cb cb, void* userdata) {
event_cb = cb;
event_cb_userdata = userdata;
}
void event_callback(hplayer_event_e e) {
if (event_cb) {
event_cb(e, event_cb_userdata);
}
}
set_event_callback使用了两个成员变量,查看头文件可以找到如下代码
class HVideoPlayer
{
protected:
hplayer_event_cb event_cb;
void* event_cb_userdata;
先看hplayer_event_cb,这个从当前头文件可以看到有如下申明
enum hplayer_event_e {
HPLAYER_OPEN_FAILED,
HPLAYER_OPENED,
HPLAYER_EOF,
HPLAYER_CLOSED,
HPLAYER_ERROR,
};
typedef int (*hplayer_event_cb)(hplayer_event_e e, void* userdata);
这里用了函数指针
如果不用typedef会导致下面的问题
1. 代码冗余,可读性差
每次声明函数指针变量或参数时,都需要重复完整的函数指针语法,导致代码冗长且难以阅读。
// 使用 typedef 的简洁写法
typedef int (*hplayer_event_cb)(hplayer_event_e e, void* userdata);
hplayer_event_cb callback; // 直接使用类型名
// 不使用 typedef 的冗余写法
int (*callback)(hplayer_event_e e, void* userdata); // 每次声明都要写完整语法
2. 函数参数声明复杂
当函数指针作为参数传递时,不使用 typedef 会导致函数声明极其臃肿,降低可读性。
// 使用 typedef 的清晰写法
void register_callback(hplayer_event_cb callback, void* userdata);
// 不使用 typedef 的复杂写法
void register_callback(int (*callback)(hplayer_event_e e, void* userdata), void* userdata);
3. 类型修改困难
4. 容易引发语法错误
5. 无法直接复用类型
6. 降低代码抽象性
typedef 的作用是为复杂类型赋予一个有意义的名称(如 hplayer_event_cb ),使代码更符合“自文档化”原则。不使用 typedef 时,代码更像是“魔法字符串”,难以理解其设计意图。
在需要 多次使用同一函数指针类型 或 提升代码可维护性 时,务必使用 typedef 。这是 C/C++ 中处理复杂类型的标准实践!
另一个event_cb_userdata就是任意数据了 void* :通用指针类型,可指向任意数据类型,提供灵活性。
上面的是pImpl_player的申明和定义,再看传入的参数
下面的代码是在(src/ui/HVideoWidget.cpp)中的,传入的this就是指HVideoWidget了
pImpl_player->set_event_callback(hplayer_event_callback, this);
传入的第一个在src/ui/HVideoWidget.h没找到,但是在HVideoWidget.cpp里找到了如下
static int hplayer_event_callback(hplayer_event_e e, void* userdata) {
HVideoWidget* wdg = (HVideoWidget*)userdata;
int custom_event_type = QCustomEvent::User;
switch (e) {
case HPLAYER_OPENED:
custom_event_type = QCustomEvent::OpenMediaSucceed;
break;
case HPLAYER_OPEN_FAILED:
custom_event_type = QCustomEvent::OpenMediaFailed;
break;
case HPLAYER_EOF:
custom_event_type = QCustomEvent::PlayerEOF;
break;
case HPLAYER_ERROR:
custom_event_type = QCustomEvent::PlayerError;
break;
default:
return 0;
}
hlogi("postEvent %d", custom_event_type);
QApplication::postEvent(wdg, new QEvent((QEvent::Type)custom_event_type));
return 0;
}
static 限制了函数的作用域为当前文件。如果是纯c++调用,可以用匿名名称空间。
static 关键字使符号(函数/变量)具有 内部链接性,每个编译单元( .c / .cpp )会生成自己的副本,链接时不会冲突。
static 在头文件中不会导致编译/链接错误,但会导致代码冗余和逻辑问题。
匿名命名空间:仅用于 .cpp 文件,定义当前文件私有的内容。 头文件:声明需要跨文件共享的接口(如类、普通命名空间中的函数)。 替代方案:需共享的工具函数应通过普通命名空间或静态成员函数实现。