Về mặt chuẩn C++, giải pháp Qt cũng hoạt động.
phát ra một tín hiệu được thực hiện bằng cách gọi một phương pháp:
emit someSignal(3.14);
Từ khóa emit
thực sự giải quyết cho một sản phẩm nào #define
, vì vậy dòng trên chỉ gọi phương thức someSignal
với các đối số cho trước. Phương pháp này có thể đã được khai báo bên trong một lớp -derived QObject
như vậy:
class SomeObject: public QObject {
Q_OBJECT
public slots:
void firstSlot() { /* implementation */ }
void secondSlot(double) { /* implementation */ }
signals:
void someSignal(double); /* no implementation here */
};
này nên dường như quen thuộc với bạn, tuy nhiên bạn có thể đã tự hỏi nơi thực hiện thực tế của các tín hiệu của bạn đến từ đâu. Như bạn có thể đoán, đây là nơi trình biên dịch đối tượng meta Qt của (MOC) đá trong Đối với mỗi phương pháp tuyên bố trong phần signals
nó cung cấp trong nguồn tạo ra nó một thực hiện rằng khoảng trông như thế này:.
void SomeObject::someSignal(double _t1)
{
void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
QMetaObject::activate(this, &staticMetaObject, 1, _a);
}
Phần thú vị là vector void *_a[]
được làm đầy với các con trỏ tới các đối số được chuyển tới tín hiệu. Không có gì đặc biệt ở đây.
Vectơ đối số được chuyển đến QMetaObject::activate
, lần lượt thực hiện một số kiểm tra an toàn luồng và các dịch vụ vệ sinh khác và sau đó bắt đầu gọi các vị trí đã được kết nối với tín hiệu, nếu có. Khi các kết nối tín hiệu tới khe được giải quyết trong thời gian chạy (cách mà connect()
hoạt động), cần một chút trợ giúp từ MOC một lần nữa. Đặc biệt, Bộ Xây dựng cũng tạo ra một qt_static_metacall()
thực hiện lớp học của bạn:
void SomeObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
SomeObject *_t = static_cast<SomeObject *>(_o);
Q_UNUSED(_t)
switch (_id) {
case 0: _t->firstSlot(); break;
case 1: _t->secondSlot((*reinterpret_cast< double(*)>(_a[1]))); break;
default: ;
}
} /* some more magic */
}
Như bạn có thể thấy, phương pháp này chứa bên kia để giải quyết các vector void *_a[]
từ trước đến một lời gọi hàm. Ngoài ra, bạn có thể thấy rằng không có danh sách đối số variadic (sử dụng dấu ba chấm, ...
) hoặc các thủ thuật có vấn đề khác có liên quan.
Vì vậy, để khai sáng câu hỏi gốc: Khi nào, ví dụ:someSignal(double)
được kết nối với secondSlot(double)
khớp với chữ ký của tín hiệu, cuộc gọi sẽ chuyển thành case 1
trong qt_static_metacall
và nó chỉ chuyển đối số như mong đợi.
Khi kết nối tín hiệu với firstSlot()
có ít đối số hơn tín hiệu, cuộc gọi sẽ giải quyết thành case 0
và firstSlot()
được gọi mà không có đối số. Đối số đã được chuyển cho tín hiệu đơn giản là không bị ảnh hưởng trong vector void *_a[]
của nó.
Điều này cũng tuân thủ bất kỳ tiêu chuẩn C++ nào hay là tính năng được đảm bảo hoạt động trên mỗi Qt? Ít nhất trong đồng bằng C, điều này dẫn đến UB qua ISO/IEC 9899: TC2 §6.5.2.2p6. – Kamajii
@Kamajii: cuộc gọi vị trí không phải là cuộc gọi hàm trực tiếp. Không có UB, Qt sẽ chăm sóc gọi hàm một cách chính xác. – Mat
Tất nhiên là những lời gọi khe chỉ là các cuộc gọi thông thường (các kết nối + -queued, vv). Khi phát xạ tín hiệu cuối cùng đạt tới thành viên qt_static_metacall' được tạo tự động, vấn đề chính phát sinh. – Kamajii