什么时候不调用覆盖的虚函数

时间:2019-08-28 15:21:01

标签: c++ inheritance mfc virtual-functions

我正在使用MFC的CRecordset类。我已经覆盖了虚拟方法DoBulkFieldExchange

通常,我的重写方法被调用并且工作正常。但是,我遇到了调用基类DoBulkFieldExchange方法的情况。

在派生的CRecordset类的不相关方法中,我调用AfxThrowDBException。这称为CRecordset析构函数。并且,在清理过程中,DoBulkFieldExchange在基类中而不是在我的派生类中被调用。在这种情况下,它会导致一个断言,因为基类不希望使用此配置调用默认版本。

我知道我的派生类已正确设置,因为它被调用了。那么在什么情况下调用基类的方法呢?

这是我自定义的CRecordset类:

class CRS : public CRecordset
{
public:
    int m_nId;
    TCHAR m_szName[CUSTOMER_NAME_MAXLENGTH + 1];

    int* m_pnIds;
    long* m_pnIdLengths;
    LPTSTR m_pszNames;
    long* m_pnNameLengths;

public:
    CRS(CDatabase* pDatabase = NULL)
        : CRecordset(pDatabase)
    {
        m_nFields = 2;

        m_nId = 0;
        m_szName[0] = '\0';

        m_pnIds = NULL;
        m_pnIdLengths = NULL;
        m_pszNames = NULL;
        m_pnNameLengths = NULL;
    }

    CString GetDefaultSQL()
    {
        return CCustomerData::m_szTableName;
    }

    void DoFieldExchange(CFieldExchange* pFX)
    {
        pFX->SetFieldType(CFieldExchange::outputColumn);
        RFX_Int(pFX, _T("Id"), m_nId);
        RFX_Text(pFX, _T("Name"), m_szName, CUSTOMER_NAME_MAXLENGTH);
    }

    void DoBulkFieldExchange(CFieldExchange* pFX)
    {
        pFX->SetFieldType(CFieldExchange::outputColumn);
        RFX_Int_Bulk(pFX, _T("Id"), &m_pnIds, &m_pnIdLengths);
        RFX_Text_Bulk(pFX, _T("Name"), &m_pszNames, &m_pnNameLengths, (CUSTOMER_NAME_MAXLENGTH + 1) * 2);
    }
};

这是使用它的代码。 ExecuteSqlQuery只是使用给定的数据库调用CRS::Open()

CRemoteDatabase db;
db.Open();
auto prs = db.ExecuteSqlQuery<CRS>(NULL, CRecordset::forwardOnly, CRecordset::useMultiRowFetch);

while (!prs->IsEOF())
{
    // The call to GetFieldValue is producing an 'Invalid cursor position'
    // error, which causes AfxThrowDBException to be called. This
    // indirectly calls the destructor, which then calls the base-class
    // DoBulkFieldExchange method, which in turn ASSERTs. Why doesn't
    // it call my derived method?
    CString sValue;
    prs->GetFieldValue((short)CUSTOMER_ID, sValue);
}

1 个答案:

答案 0 :(得分:1)

当基类析构函数调用虚拟函数时,可能会调用基类方法而不是派生类方法。在这种情况下,派生类已被销毁,无法调用任何虚拟方法。 (有关this question的更多信息)。

回到您的问题:

使用VS 2019(14.22.27905)从MFC代码中获取dbcore.cpp:

CRecordset::FreeRowset()调用DoBulkFieldExchange,在某些情况下FreeRowset()的析构函数调用CRecordset

这是CRecordset::FreeRowset代码中的注释。

  

调用虚函数DoBulkFieldExchange,这很不好    因为Close可能会从析构函数调用FreeRowset。    如果RFX_Bulk函数可以    内存分配。最终结果是用户必须致电    明确Close    (而不是依赖于析构函数)(如果使用多行读取),    否则会导致内存泄漏。如果行集已经分配,    删除旧的行集缓冲区