【问题标题】:Accessing sub class's member functions through base class pointer通过基类指针访问子类的成员函数
【发布时间】:2015-09-25 21:12:44
【问题描述】:

我正在尝试编写一个使用 DOM 将注册列表写入 XML 文件的类。注册列表包含三种类型的注册Registration,这是标准基本注册类GuestRegistrationStudentRegistration,它们都派生自Registration

GuestRegistration 班级有一个唯一成员 category,学生注册有一个唯一成员 qualification

当我遍历注册指针列表时,我只能访问基类Registration 的成员函数。有什么方法可以访问子类的数据成员以使用getter 函数getCategorygetQualification?每当类名与两者中的任何一个匹配时,我都尝试创建 GuestRegistrationStudentRegistration 指针,但会出现指针转换错误。

void RegistrationListWriter::write(RegistrationList r) {
    QList<Registration*> regList = r.getRegistrationList();

    for (int i = 0; i < regList.size(); ++i) {
        QString cn = regList.at(i)->metaObject()->className();
        Person tempPerson = regList.at(i)->getAttendee();

        appendRegistrationAndType(cn);
        appendAttendee(tempPerson);
        //this is where my issue starts
        if (cn == "GuestRegistration") {
            GuestRegistration guestReg = regList.at(i);
            appendAttendeeCatagory(guestReg.getCatagory());
        }

        if (cn == "StudentRegistration") {
            StudentRegistration* stuReg = regList.at(i);
            appendAttendeeQualification(stuReg->getQualification());
        }

        appendBookingDate(regList.at(i)->getBookingDate().toString());
        appendRegistrationFee(regList.at(i)->calculateFee());

    }
}

【问题讨论】:

  • 您应该能够对两种指针类型执行dynamic_cast,如果您返回除nullptr 以外的任何内容,您将可以访问子类方法。

标签: c++ qt


【解决方案1】:

您可以使用dynamic_cast 来检查特定的子类:

void RegistrationListWriter::write(RegistrationList r) {
    QList<Registration*> regList = r.getRegistrationList();

    for (int i = 0; i < regList.size(); ++i) {
        Registration *reg = regList.at(i);

        appendRegistrationAndType(reg->metaObject()->className());
        appendAttendee(reg->getAttendee());

        if (GuestRegistration *guestReg = dynamic_cast<GuestRegistration*>(reg)) {
            appendAttendeeCatagory(guestReg->getCatagory());
        }
        else
        if (StudentRegistration* stuReg = dynamic_cast<StudentRegistration*>(reg)) {
            appendAttendeeQualification(stuReg->getQualification());
        }
        // and so on ...

        appendBookingDate(reg->getBookingDate().toString());
        appendRegistrationFee(reg->calculateFee());    
    }
}

但是,我建议在 Registration 类本身中实现一个虚拟方法,您的子类可以覆盖该虚拟方法以根据需要注册其他项目,例如:

class Registration {
    ...
    virtual void appendExtraAttendees(RegistrationListWriter *writer){}
    ...
};

class GuestRegistration : public Registration {
    ...
    virtual void appendExtraAttendees(RegistrationListWriter *writer);
    ...
};

void GuestRegistration::appendExtraAttendees(RegistrationListWriter *writer){
    writer->appendAttendeeCatagory(getCatagory());
}

class StudentRegistration : public Registration {
    ...
    virtual void appendExtraAttendees(RegistrationListWriter *writer);
    ...
};

void StudentRegistration::appendExtraAttendees(RegistrationListWriter *writer){
    writer->appendAttendeeQualification(getQualification());
}

void RegistrationListWriter::write(RegistrationList r) {
    QList<Registration*> regList = r.getRegistrationList();

    for (int i = 0; i < regList.size(); ++i) {
        Registration *reg = regList.at(i);

        appendRegistrationAndType(reg->metaObject()->className());
        appendAttendee(reg->getAttendee());

        reg->appendExtraAttendees(this);

        appendBookingDate(reg->getBookingDate().toString());
        appendRegistrationFee(reg->calculateFee());    
    }
}

或者:

class Registration {
    ...
    virtual void appendAttendees(RegistrationListWriter *writer);
    ...
};

void Registration::appendAttendees(RegistrationListWriter *writer){
    writer->appendAttendee(getAttendee());
}

class GuestRegistration : public Registration {
    ...
    virtual void appendAttendees(RegistrationListWriter *writer);
    ...
};

void GuestRegistration::appendAttendees(RegistrationListWriter *writer){
    Registration::appendAttendees(writer);
    writer->appendAttendeeCatagory(getCatagory());
}

class StudentRegistration : public Registration {
    ...
    virtual void appendAttendees(RegistrationListWriter *writer);
    ...
};

void StudentRegistration::appendAttendees(RegistrationListWriter *writer){
    Registration::appendAttendees(writer);
    writer->appendAttendeeQualification(getQualification());
}

void RegistrationListWriter::write(RegistrationList r) {
    QList<Registration*> regList = r.getRegistrationList();

    for (int i = 0; i < regList.size(); ++i) {
        Registration *reg = regList.at(i);

        appendRegistrationAndType(reg->metaObject()->className());

        reg->appendAttendees(this);

        appendBookingDate(reg->getBookingDate().toString());
        appendRegistrationFee(reg->calculateFee());    
    }
}

【讨论】:

  • 非常感谢您非常详细的回答@Remy Lebeau 为什么创建虚函数比使用dynamic_cast 或QObject_cast 更好?
  • dynamic_cast 有开销,因为它必须在运行时执行 RTTI 查找以确定源对象是否与请求的类相关并返回适当的指针。此外,如果您将来添加更多派生类,则必须添加更多 dynamic_cast 检查。使用虚方法调用,可以避免查找。调用虚方法是微不足道的,只是对对象 vtable 的简单索引,因此调用会在运行时直接跳转到最派生的实现。效率更高,调用代码不必关心派生类的细节。
【解决方案2】:

简单的 C++ 工具是 dynamic_cast()。

一般来说,最初设计一个需要这种演员阵容的项目并不是一个好的做法。可以考虑各种设计模式。

我看到您使用的是metaObject(),所以这意味着Registration 具有QObject 作为基类。在这种情况下,可以使用qobject_cast:

qobject_cast() 函数的行为类似于标准 C++ dynamic_cast(),其优点是不需要 RTTI 支持并且它可以跨动态库边界工作。

【讨论】:

  • 当我使用 QObject_cast 我得到以下错误: C:\Qt\Qt5.3.0\5.3\mingw482_32\include\QtCore\qobject.h:-1: In copy constructor 'RegistrationList::RegistrationList (const RegistrationList&)': C:\Qt\Qt5.3.0\5.3\mingw482_32\include\QtCore\qobject.h:465: 错误: 'QObject::QObject(const QObject&)' 是私有的 Q_DISABLbE_COPY(QObject) ^跨度>
  • @user2094257 您不能将基类实例强制转换为派生实例。只能转换指针:qobject_cast&lt;GuestRegistration*&gt;(base_ptr),其中base_ptr 是指向基类实例(Registration*) 的指针。
【解决方案3】:

除了使用dynamic_cast,您可以让基类提供一个接口,派生类使用该接口来写入其特定于类的数据。

【讨论】:

    【解决方案4】:

    可能您需要将方法设为虚拟。 非虚方法使用编译时使用的类的方法,运行时选择子类的虚方法。

    【讨论】:

      猜你喜欢
      • 2021-08-05
      • 1970-01-01
      • 2011-02-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多