前言
Qt 的信號槽機制雖強大,但依賴 MOC 預處理。若需在非 Qt 專案中使用輕量級、純 C++ 的信號槽庫,sigslot.h(單頭文件、簡單易用)與 sigc++(功能豐富、支援更多特性)是兩大主流選擇。本文詳解兩者在 Visual Studio 環境下的配置與核心用法。
sigslot.h:極簡信號槽實現
獲取與配置
- 官方地址:sigslot.sourceforge.net
- VS2013 修正:在
sigslot.h 第 420 行,將
typedef sender_set::const_iterator const_iterator;
改為
typedef typename sender_set::const_iterator const_iterator;
核心概念
| 概念 | 說明 |
signal0 ~ signal8 | 信號類,模板參數對應 0~8 個參數,作為類成員 |
has_slots<> | 槽類需公開繼承此基類 |
| 槽函數 | 返回值必須為 void,參數與對應信號一致 |
| 連接 | signal.connect(&slotObj, &SlotClass::func) |
| 斷開 | disconnect() / disconnect_all() |
| 發射 | signal.emit(args...) 或直接 signal(args...) |
完整範例:互動式物件控制
#include
#include
#include
#include
using namespace std;
using namespace sigslot;
enum ObjState { Normal = 0, Hidden = 1 };
class ObjectBase : public has_slots<> {
int px, py;
ObjState state;
public:
ObjectBase(int x=0, int y=0) : px(x), py(y), state(Normal) {}
void onMoveBy(const int& x, const int& y) { px += x; py += y; }
void onResize(const int& x, const int& y) { px = x; py = y; }
void onSetState(ObjState s) { state = s; }
void onPrintState() {
cout << (state == Normal ? "Normal" : "Hidden") << endl;
}
int getX() const { return px; }
int getY() const { return py; }
};
class Control {
public:
signal2 moveBy;
signal2 resize;
signal1 setState;
signal0<> printState;
};
int main() {
ObjectBase obj(10, 10);
Control ctrl;
ctrl.moveBy.connect(&obj, &ObjectBase::onMoveBy);
ctrl.resize.connect(&obj, &ObjectBase::onResize);
ctrl.setState.connect(&obj, &ObjectBase::onSetState);
ctrl.printState.connect(&obj, &ObjectBase::onPrintState);
while (true) {
system("cls");
cout << "Object Info\n Pos: (" << obj.getX() << ", " << obj.getY() << ")\n State: " << obj.getState() << "\n";
cout << "Commands: m/M(move), r/R(resize), s/S(set state), p/P(print), q/Q(quit)\nInput: ";
char ch = _getch();
switch (ch) {
case 'm': ctrl.moveBy(10, 10); break;
case 'M': ctrl.moveBy.emit(-20, -20); break;
case 'r': case 'R': ctrl.resize.emit(0, 0); break;
case 's': ctrl.setState(Normal); break;
case 'S': ctrl.setState.emit(Hidden); break;
case 'p': case 'P': ctrl.printState.emit(); _getch(); break;
case 'q': case 'Q': return 0;
}
}
}
sigc++:功能更強大的信號槽框架
編譯與配置要點
- 官方地址:libsigc.sourceforge.net
- VS 高版本編譯常見問題:
- 缺少
msvc_recommended_pragmas.h:專案屬性 → C/C++ → 高級 → 強制包含文件 中移除,或自行添加空文件。
- 缺少
slot.cc 等文件:可從低版本複製至對應目錄。
- 專案設定:包含目錄加入 sigc++ 路徑,鏈接庫文件(Debug:
sigcd.lib,Release: sigc.lib)。
核心 API 對比
| 特性 | sigslot | sigc++ |
| 信號聲明 | signalN<Params> | sigc::signal<Ret, Params> 或 signalN |
| 槽類基類 | has_slots<> | sigc::trackable |
| 連接全局函數 | 直接取址 | sigc::ptr_funN |
| 連接成員函數 | 直接取址 | sigc::mem_funN / sigc::mem_fun(*this, &Class::func) |
| 連接對象 | sigc::connection | sigc::connection(可選,析構時自動斷開) |
| 發射 | emit() 或 () | emit() |
sigc++ 完整範例
#include
#include
#include
#include
using namespace std;
#ifdef _DEBUG
#pragma comment(lib, "sigc280d.lib")
#else
#pragma comment(lib, "sigc280.lib")
#endif
enum ObjState { Normal = 0, Hidden = 1 };
class ObjectBase : public sigc::trackable {
int px, py;
ObjState state;
sigc::signal putMsg;
public:
ObjectBase(int x=0, int y=0) : px(x), py(y), state(Normal) {
putMsg.connect(sigc::mem_fun(*this, &ObjectBase::printMsg));
}
void onMoveBy(const int& x, const int& y) { px += x; py += y; }
void onResize(const int& x, const int& y) { px = x; py = y; }
void onSetState(ObjState s) { state = s; }
void onPrintState() { cout << (state == Normal ? "Normal" : "Hidden") << endl; }
int getX() const { return px; }
int getY() const { return py; }
private:
void printMsg(const char* str) { cout << str << endl; }
};
class Control {
public:
sigc::signal moveBy;
sigc::signal resize;
sigc::signal setState;
};
int printSpaceStr(const char* str) {
for (const char* p = str; *p; ++p) cout << *p << ' ';
cout << endl;
return 0;
}
int main() {
ObjectBase obj(0, 0);
Control ctrl;
sigc::signal printSS;
printSS.connect(sigc::ptr_fun(&printSpaceStr));
sigc::signal printState;
printState.connect(sigc::mem_fun(obj, &ObjectBase::onPrintState));
ctrl.moveBy.connect(sigc::mem_fun(obj, &ObjectBase::onMoveBy));
ctrl.resize.connect(sigc::mem_fun(obj, &ObjectBase::onResize));
ctrl.setState.connect(sigc::mem_fun(obj, &ObjectBase::onSetState));
while (true) {
system("cls");
obj.putMsg.emit("Object Information");
cout << " Pos: (" << obj.getX() << ", " << obj.getY() << ")\n";
cout << "Commands: m/M(move), r/R(resize), s/S(state), p/P(print), a/A(space), q/Q(quit)\nInput: ";
char ch = _getch();
switch (ch) {
case 'm': ctrl.moveBy.emit(10, 10); break;
case 'M': ctrl.moveBy.emit(-20, -20); break;
case 'r': case 'R': ctrl.resize.emit(0, 0); break;
case 's': ctrl.setState.emit(Normal); break;
case 'S': ctrl.setState.emit(Hidden); break;
case 'p': case 'P': printState.emit(); _getch(); break;
case 'a': case 'A': printSS.emit("Sigc_Demo"); _getch(); break;
case 'q': case 'Q': return 0;
}
}
}
選型建議
- sigslot:單文件、零依賴、適合嵌入式或輕量專案,API 直觀。
- sigc++:支援綁定、適配器、類型安全、自動生命週期管理(trackable),適合大型專案。