将 QObject 转换为 void 指针或从 void 指针转换会丢失数据?
Casting QObject to and from void pointer looses data?
我尝试将我的对象与空指针相互转换。
这就是我的 class 继承的方式:
#ifndef EVERYTHING_H
#define EVERYTHING_H
#include <QString>
#include <QObject>
struct Base
{
public:
const QString name;
explicit Base(const QString& n)
: name { n } {}
};
class A : public QObject, public Base
{
Q_OBJECT
public:
explicit A(QObject *parent = nullptr)
: QObject(parent),
Base("name") {}
};
#endif // EVERYTHING_H
这是我的主要内容:
#include <QApplication>
#include <QDebug>
#include "everything.h"
static A *a = new A();
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
qDebug() << qint64(a);
qDebug() << a->name;
auto aa = static_cast<Base*>(a);
qDebug() << qint64(aa);
qDebug() << aa->name;
void* v = static_cast<void*>(a);
aa = static_cast<Base*>(v);
qDebug() << qint64(v);
qDebug() << qint64(aa);
qDebug() << aa->name;
return app.exec();
}
输出为:
1983584
"name"
1983600
"name"
1983584
1983584
""
- 我不明白为什么第一个
aa
与原始指针的地址不同并且可以工作,而第二个 aa
突然指向原始地址但不起作用。
- 常量数据怎么会在这里丢失?在我的实际应用程序(不是这个最小的例子)中,我没有得到一个空字符串,而是一个断言错误:
ASSERT: "size == 0 || offset < 0 || size_t(offset) >= sizeof(QArrayData)"
注意:如果 A 不是 QObject(只是 Base),一切正常,我每次都得到“name”。
我需要先从 A 施法到 Base,然后从 Base 施法到 void,然后再从 void 施法到 Base。演员的顺序显然需要颠倒。
在继承层次结构中向上或向下转换指针时,不能保证指针中的底层内存地址保持不变。根据所涉及的继承细节,可能需要进行调整以确保生成的指针最终指向对象的正确部分。 (例如,这种事情可以发生在多重继承中。)
通常情况下,编译器会为您处理这些细节,但是当您涉及 void*
时,您会阻止编译器知道发生了什么。因此,正确地做事完全取决于您。
在这种情况下:
A *a = new A();
auto aa = static_cast<Base*>(a);
编译器知道要进行哪些调整才能生成有效的 Base*
然而这里:
A *a = new A();
void* v = static_cast<void*>(a);
aa = static_cast<Base*>(v);
编译器获取a
中的内存地址,直接复制到v
,然后直接复制到aa
。没有进行任何调整。所以通过颠覆编译器的类型知识,你最终遇到了问题。
我很确定我听说过您应该只从 void*
转换回最初转换为 void*
的确切类型。这种情况就是原因的一个例子。所以如果我们改变它:
A *a = new A();
void* v = static_cast<void*>(a);
A* aa = static_cast<A*>(v);
Base* base = static_cast<Base*>(aa);
那么你应该会发现它有效。
我尝试将我的对象与空指针相互转换。
这就是我的 class 继承的方式:
#ifndef EVERYTHING_H
#define EVERYTHING_H
#include <QString>
#include <QObject>
struct Base
{
public:
const QString name;
explicit Base(const QString& n)
: name { n } {}
};
class A : public QObject, public Base
{
Q_OBJECT
public:
explicit A(QObject *parent = nullptr)
: QObject(parent),
Base("name") {}
};
#endif // EVERYTHING_H
这是我的主要内容:
#include <QApplication>
#include <QDebug>
#include "everything.h"
static A *a = new A();
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
qDebug() << qint64(a);
qDebug() << a->name;
auto aa = static_cast<Base*>(a);
qDebug() << qint64(aa);
qDebug() << aa->name;
void* v = static_cast<void*>(a);
aa = static_cast<Base*>(v);
qDebug() << qint64(v);
qDebug() << qint64(aa);
qDebug() << aa->name;
return app.exec();
}
输出为:
1983584
"name"
1983600
"name"
1983584
1983584
""
- 我不明白为什么第一个
aa
与原始指针的地址不同并且可以工作,而第二个aa
突然指向原始地址但不起作用。 - 常量数据怎么会在这里丢失?在我的实际应用程序(不是这个最小的例子)中,我没有得到一个空字符串,而是一个断言错误:
ASSERT: "size == 0 || offset < 0 || size_t(offset) >= sizeof(QArrayData)"
注意:如果 A 不是 QObject(只是 Base),一切正常,我每次都得到“name”。
我需要先从 A 施法到 Base,然后从 Base 施法到 void,然后再从 void 施法到 Base。演员的顺序显然需要颠倒。
在继承层次结构中向上或向下转换指针时,不能保证指针中的底层内存地址保持不变。根据所涉及的继承细节,可能需要进行调整以确保生成的指针最终指向对象的正确部分。 (例如,这种事情可以发生在多重继承中。)
通常情况下,编译器会为您处理这些细节,但是当您涉及 void*
时,您会阻止编译器知道发生了什么。因此,正确地做事完全取决于您。
在这种情况下:
A *a = new A();
auto aa = static_cast<Base*>(a);
编译器知道要进行哪些调整才能生成有效的 Base*
然而这里:
A *a = new A();
void* v = static_cast<void*>(a);
aa = static_cast<Base*>(v);
编译器获取a
中的内存地址,直接复制到v
,然后直接复制到aa
。没有进行任何调整。所以通过颠覆编译器的类型知识,你最终遇到了问题。
我很确定我听说过您应该只从 void*
转换回最初转换为 void*
的确切类型。这种情况就是原因的一个例子。所以如果我们改变它:
A *a = new A();
void* v = static_cast<void*>(a);
A* aa = static_cast<A*>(v);
Base* base = static_cast<Base*>(aa);
那么你应该会发现它有效。