什么时候不调用重写的虚函数
When is an overridden virtual function not called
我正在使用 MFC 的 CRecordset
class。我已经重写了虚拟方法 DoBulkFieldExchange
.
一般来说,我重写的方法被调用并且工作正常。但是,我有一种情况是调用基本 class DoBulkFieldExchange
方法。
在我派生的CRecordset
class的一个不相关的方法中,我调用了AfxThrowDBException
。这会调用 CRecordset
析构函数。而且,在清理期间,DoBulkFieldExchange
在基 class 中调用,而不是在我的派生 class 中调用。在这种情况下,它会导致断言,因为基础 class 不希望使用此配置调用默认版本。
我知道派生的 class 设置正确,因为它被调用了。那么在什么情况下会调用baseclass'方法呢?
这是我的习惯 CRecordset
class:
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] = '[=10=]';
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);
}
当基本 class 析构函数调用虚函数时,可能会调用基本 class 方法而不是派生的 class 方法。在这种情况下,派生的 class 已经被销毁,无法调用虚方法。 (有关 this question 的更多信息)。
回到你的问题:
来自 MFC 代码,dbcore.cpp 使用 VS 2019 (14.22.27905):
CRecordset::FreeRowset()
调用 DoBulkFieldExchange
,看起来在某些情况下 FreeRowset()
被 CRecordset
.
的析构函数调用
这是来自 CRecordset::FreeRowset
代码的注释。
Calling virtual function, DoBulkFieldExchange
, here is bad
because Close
then FreeRowset
may get called from destructor.
There is no simple choice however if RFX_Bulk
functions do
a memory allocation. The net result is that users MUST call
Close
explicitly
(rather than relying on destructor) if using multi row fetches,
otherwise they will get a memory leak. If rowset already allocated,
delete old rowset buffers
我正在使用 MFC 的 CRecordset
class。我已经重写了虚拟方法 DoBulkFieldExchange
.
一般来说,我重写的方法被调用并且工作正常。但是,我有一种情况是调用基本 class DoBulkFieldExchange
方法。
在我派生的CRecordset
class的一个不相关的方法中,我调用了AfxThrowDBException
。这会调用 CRecordset
析构函数。而且,在清理期间,DoBulkFieldExchange
在基 class 中调用,而不是在我的派生 class 中调用。在这种情况下,它会导致断言,因为基础 class 不希望使用此配置调用默认版本。
我知道派生的 class 设置正确,因为它被调用了。那么在什么情况下会调用baseclass'方法呢?
这是我的习惯 CRecordset
class:
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] = '[=10=]';
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);
}
当基本 class 析构函数调用虚函数时,可能会调用基本 class 方法而不是派生的 class 方法。在这种情况下,派生的 class 已经被销毁,无法调用虚方法。 (有关 this question 的更多信息)。
回到你的问题:
来自 MFC 代码,dbcore.cpp 使用 VS 2019 (14.22.27905):
CRecordset::FreeRowset()
调用 DoBulkFieldExchange
,看起来在某些情况下 FreeRowset()
被 CRecordset
.
这是来自 CRecordset::FreeRowset
代码的注释。
Calling virtual function,
DoBulkFieldExchange
, here is bad becauseClose
thenFreeRowset
may get called from destructor. There is no simple choice however ifRFX_Bulk
functions do a memory allocation. The net result is that users MUST callClose
explicitly (rather than relying on destructor) if using multi row fetches, otherwise they will get a memory leak. If rowset already allocated, delete old rowset buffers