c++ - 什么时候不调用重写的虚函数

标签 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);
}

最佳答案

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

回到你的问题:

来自 MFC 代码,使用 VS 2019 (14.22.27905) 的 dbcore.cpp:

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

关于c++ - 什么时候不调用重写的虚函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57695639/

相关文章:

c++ - 两个线程之间通过事件的一种方式通信

c++ - 具有单个互斥体的 std::scoped_lock 行为

android - 在 Qt Android 应用程序中使用 Holo 主题

java - 为什么不能在通用 catch 子句中捕获自定义异常

javascript - 如何在 JavaScript 中继承私有(private)成员?

c++ - 如何在 MFC 中创建预呈现输入窗口?

c++ - 从模板函数中取消引用的迭代器推断类型

css - 在 CSS 中使用对象引用

c++ - 从资源 ID 创建 CBitmap

c++ - MFC TRY CATCH 与 C++ try catch for MFC 异常