我正在使用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);
}
答案 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
(而不是依赖于析构函数)(如果使用多行读取), 否则会导致内存泄漏。如果行集已经分配, 删除旧的行集缓冲区