成员对象中枚举的行为

C++ - Behavior of enums in member object

本文关键字:枚举 成员对象      更新时间:2023-10-16

我在c++中使用Qt,我正在与枚举作斗争。考虑下面这样的情况:

克隆到GitHub: https://github.com/jif/enum

// memberclass.h =======================================================
#ifndef MEMBERCLASS_H
#define MEMBERCLASS_H
#include <QObject>
class MemberClass : public QObject
{
    Q_OBJECT
public:
    enum ErrorType {
        NoError,
        IsError
    };
    explicit MemberClass(QObject *parent = 0);
    void setError(ErrorType errorType);
    MemberClass::ErrorType error() const;
    void otherMethod();
private:
    MemberClass::ErrorType mError;
};
#endif // MEMBERCLASS_H
// memberclass.cpp =======================================================
#include "memberclass.h"
#include <QDebug>
MemberClass::MemberClass(QObject *parent) :
    QObject(parent)
{
    mError = NoError;
    qDebug() << "mError initialized.";
}
MemberClass::ErrorType MemberClass::error() const {
    return mError;
}
void MemberClass::setError(ErrorType errorType) {
    mError = errorType;
}
void MemberClass::otherMethod() {
    qDebug() << "    In otherMethod()...";
    qDebug() << "      mError = " << mError;
    qDebug() << "      NoError = " << NoError;
    qDebug() << "      IsError = " << IsError;
    qDebug() << "    End otherMethod()";
}
// parentclass.h =======================================================
#ifndef PARENTCLASS_H
#define PARENTCLASS_H
#include <QObject>
#include "memberclass.h"
class ParentClass : public QObject
{
    Q_OBJECT
public:
    explicit ParentClass(QObject *parent = 0);
    void testEnumStuff();
private:
    MemberClass objectMember;
    MemberClass *pointerMember;
};
#endif // PARENTCLASS_H
// parentclass.cpp =======================================================
#include "parentclass.h"
#include <QDebug>
ParentClass::ParentClass(QObject *parent) :
    QObject(parent)
{
    pointerMember = new MemberClass(this);
}
void ParentClass::testEnumStuff() {
    qDebug() << "Just initialized...";
    qDebug() << "  pointerMember::mError = " << pointerMember->error();
    qDebug() << "  objectMember::mError =  " << objectMember.error();
    qDebug() << "Calling otherMethod() on each member...";
    qDebug() << "  In pointerMember...";
    pointerMember->otherMethod();
    qDebug() << "  In objectMember...";
    objectMember.otherMethod();
    qDebug() << "  pointerMember::mError = " << pointerMember->error();
    qDebug() << "  objectMember::mError =  " << objectMember.error();
    qDebug() << "Done.";
}
// main.cpp =======================================================
#include <QCoreApplication>
#include "parentclass.h"
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    ParentClass parent;
    parent.testEnumStuff();
    return a.exec();
}

// enum.pro =======================================================
QT       += core
QT       -= gui
TARGET = enum
CONFIG   += console
CONFIG   -= app_bundle
TEMPLATE = app

SOURCES += main.cpp 
    parentclass.cpp 
    memberclass.cpp
HEADERS += 
    parentclass.h 
    memberclass.h

使用类型为ErrorType的变量mError不能按预期工作(它在执行过程中获取奇怪且不一致的值)。

我得到这样的输出:

mError initialized.
mError initialized.    
Just initialized... 
  pointerMember::mError =  0 
  objectMember::mError =   0 
Calling otherMethod() on each member... 
  In pointerMember... 
    In otherMethod()... 
      mError =  0 
      NoError =  0 
      IsError =  1 
    End otherMethod() 
  In objectMember... 
    In otherMethod()... 
      mError =  13498688 
      NoError =  0 
      IsError =  1 
    End otherMethod() 
  pointerMember::mError =  0 
  objectMember::mError =   13498688 
Done.

您没有为MemberClass定义默认构造函数,编译器提供的隐式构造函数用于在ParentClass中初始化objectMember。隐式构造函数不会为您初始化mError,因此您得到的是随机值。

MemberClass添加显式默认构造函数:

MemberClass::MemberClass() :
    QObject(NULL), mError(NoError)
{
}

ParentClass的成员初始化:

ParentClass::ParentClass(QObject *parent) :
    QObject(parent), objectMember(NULL)
{
    pointerMember = new MemberClass(this);
}

在这里声明成员

private:
    MemberClass objectMember;

没有为构造函数提供参数。它正在调用QObject的默认构造函数(它不在您的代码中),并且没有初始化枚举。

我只是复制并粘贴您的代码到一个干净的项目。当使用MSVC2010用Qt 4.8.0编译时,一切对我来说都很正常。您重新构建项目并重新运行qmake了吗?

我克隆了你的代码,运行它,没有问题。
我建议您清理项目并重新构建它。代码看起来也不错。我认为编译器缓存在代码修改期间被损坏(有时会发生),并且生成了错误的代码,清洁项目应该修复它。

实际上您并没有得到一个自动生成的构造函数。这样做的原因是,您只会在根本没有定义构造函数的类中获得这样的构造函数。您为MemberClass(QObject *parent = 0)定义了一个构造函数,但是因为它已经存在,所以没有为您分配一个自动生成的noargs构造函数。

一个类似但相关的例子可以说明这类问题:

class Base
{
    /*Has a constructor therefore does not get a free 
     noargs constructor*/
    Base(int i) {}; 
};
class Derived : public Base
{
};
int main()
{
     /*Not actually possible because Derived will get a free noargs constructor
       which will attempt to invoke the non existant no args constructor of Base.
     */
     Derived d; 
     return 0;
}

你可以在这里看到结果

如果您在堆栈上定义对象,如:

MemberOfMyClass member;

当您超出作用域时,您的对象信息丢失

MemberClass单参数构造函数有<<"mError初始化。";这在输出中是没有的。编译器生成的默认构造函数不知道成员变量的期望值,所以如果为这些变量分配的内存有垃圾,这些垃圾就会泄露。

你从来没有在你的ParentClass的构造函数中初始化"objectMember"值,但是你初始化了"pointerMember"。我敢打赌,"objectMember"也是在测试中给您带来问题的值,这不是巧合。我没有QT,但是如果你添加

会发生什么?
objectmember = *pointerMember;

初始化后的构造函数我已经有一段时间没有使用c++了,所以我可能错过了一些明显的东西,但我敢打赌这是你的问题。

还请注意,上次我使用它时,Visual Studio的调试器倾向于为程序员设置所有未初始化的值为0。一旦代码被实际编译,这种情况就不会发生(尽管我相信有些编译器可能会这样做,比如GCC),所以这可能就是您看到此错误而其他编译器没有看到此错误的原因。因此,它可能有助于使您正在查找的值为"IsError" -这样,您将查找的值将为1,而不是默认的未初始化值。