我试图解决能够迭代几个不同的文本数组的问题,所有这些文本数组都存储在一个很大的内存驻留数据库中,struct。
以下是在 MFC 测试应用程序上使用 Visual Studio 2017 社区版制定的。我将其作为示例包括在内,因为此帖子是我遇到的几个提供了一些帮助但仍不足以满足我的需求的帖子之一。
包含内存驻留数据的struct 如下所示。为简洁起见,我删除了大部分元素,也没有包括使用的预处理器定义(使用的 SDK 用于 C 和 C++,并且是旧的)。
我感兴趣的是为各种 WCHAR 二维数组创建迭代器,其中包含用于助记符的文本字符串。
typedef struct tagUNINTRAM {
// stuff deleted ...
WCHAR ParaTransMnemo[MAX_TRANSM_NO][PARA_TRANSMNEMO_LEN]; /* prog #20 */
WCHAR ParaLeadThru[MAX_LEAD_NO][PARA_LEADTHRU_LEN]; /* prog #21 */
WCHAR ParaReportName[MAX_REPO_NO][PARA_REPORTNAME_LEN]; /* prog #22 */
WCHAR ParaSpeMnemo[MAX_SPEM_NO][PARA_SPEMNEMO_LEN]; /* prog #23 */
WCHAR ParaPCIF[MAX_PCIF_SIZE]; /* prog #39 */
WCHAR ParaAdjMnemo[MAX_ADJM_NO][PARA_ADJMNEMO_LEN]; /* prog #46 */
WCHAR ParaPrtModi[MAX_PRTMODI_NO][PARA_PRTMODI_LEN]; /* prog #47 */
WCHAR ParaMajorDEPT[MAX_MDEPT_NO][PARA_MAJORDEPT_LEN]; /* prog #48 */
// ... stuff deleted
} UNINIRAM;
当前的方法是使用模板为每个数组定义一个代理类,然后拥有一个迭代器类,该类可用于通过使用代表该数组的代理对象来迭代特定数组。
内存常驻数据的副本存储在一个对象中,该对象处理从/向磁盘读取和写入内存常驻数据。此类CFilePara 包含模板化代理类(MnemonicIteratorDimSize 及其派生的子类MnemonicIteratorDimSizeBase)和迭代器类MnemonicIterator。
创建的代理对象附加到一个迭代器对象,该迭代器对象通过一个基类描述的接口访问必要的信息,所有代理类都从该基类派生。结果是有一个单一类型的迭代器类,它可以与多个不同的代理类一起使用,因为不同的代理类都公开了相同的接口,即代理基类的接口。
第一件事是创建一组标识符,这些标识符将提供给类工厂以生成该类型助记符的特定代理对象。这些标识符用作用户界面的一部分,以识别用户有兴趣查看和可能修改的特定供应数据。
const static DWORD_PTR dwId_TransactionMnemonic = 1;
const static DWORD_PTR dwId_ReportMnemonic = 2;
const static DWORD_PTR dwId_SpecialMnemonic = 3;
const static DWORD_PTR dwId_LeadThroughMnemonic = 4;
代理类
模板化代理类及其基类如下。我需要容纳几种不同类型的wchar_t 文本字符串数组。二维数组具有不同数量的助记符,具体取决于助记符的类型(目的),并且不同类型的助记符具有不同的最大长度,在 5 个文本字符和 20 个文本字符之间变化。派生代理类的模板自然适合需要每个助记符中最大字符数的模板。代理对象创建完成后,我们再使用SetRange()方法来指定实际的助记符数组及其范围。
// proxy object which represents a particular subsection of the
// memory resident database each of which is an array of wchar_t
// text arrays though the number of array elements may vary.
class MnemonicIteratorDimSizeBase
{
DWORD_PTR m_Type;
public:
MnemonicIteratorDimSizeBase(DWORD_PTR x) { }
virtual ~MnemonicIteratorDimSizeBase() { }
virtual wchar_t *begin() = 0;
virtual wchar_t *end() = 0;
virtual wchar_t *get(int i) = 0;
virtual int ItemSize() = 0;
virtual int ItemCount() = 0;
virtual DWORD_PTR ItemType() { return m_Type; }
};
template <size_t sDimSize>
class MnemonicIteratorDimSize : public MnemonicIteratorDimSizeBase
{
wchar_t (*m_begin)[sDimSize];
wchar_t (*m_end)[sDimSize];
public:
MnemonicIteratorDimSize(DWORD_PTR x) : MnemonicIteratorDimSizeBase(x), m_begin(0), m_end(0) { }
virtual ~MnemonicIteratorDimSize() { }
virtual wchar_t *begin() { return m_begin[0]; }
virtual wchar_t *end() { return m_end[0]; }
virtual wchar_t *get(int i) { return m_begin[i]; }
virtual int ItemSize() { return sDimSize; }
virtual int ItemCount() { return m_end - m_begin; }
void SetRange(wchar_t (*begin)[sDimSize], wchar_t (*end)[sDimSize]) {
m_begin = begin; m_end = end;
}
};
迭代器类
迭代器类本身如下。这个类只提供了基本的前向迭代器功能,这是目前所需要的。但是,我希望当我需要额外的东西时,这会改变或扩展。
class MnemonicIterator
{
private:
MnemonicIteratorDimSizeBase *m_p; // we do not own this pointer. we just use it to access current item.
int m_index; // zero based index of item.
wchar_t *m_item; // value to be returned.
public:
MnemonicIterator(MnemonicIteratorDimSizeBase *p) : m_p(p) { }
~MnemonicIterator() { }
// a ranged for needs begin() and end() to determine the range.
// the range is up to but not including what end() returns.
MnemonicIterator & begin() { m_item = m_p->get(m_index = 0); return *this; } // begining of range of values for ranged for. first item
MnemonicIterator & end() { m_item = m_p->get(m_index = m_p->ItemCount()); return *this; } // end of range of values for ranged for. item after last item.
MnemonicIterator & operator ++ () { m_item = m_p->get(++m_index); return *this; } // prefix increment, ++p
MnemonicIterator & operator ++ (int i) { m_item = m_p->get(m_index++); return *this; } // postfix increment, p++
bool operator != (MnemonicIterator &p) { return **this != *p; } // minimum logical operator is not equal to
wchar_t * operator *() const { return m_item; } // dereference iterator to get what is pointed to
};
代理对象工厂根据助记符确定创建哪个对象。创建代理对象,返回的指针是标准的基类类型,以便无论访问哪个不同的助记符部分都具有统一的接口。 SetRange()方法用于向代理对象指定代理所代表的具体数组元素以及数组元素的范围。
CFilePara::MnemonicIteratorDimSizeBase * CFilePara::MakeIterator(DWORD_PTR x)
{
CFilePara::MnemonicIteratorDimSizeBase *mi = nullptr;
switch (x) {
case dwId_TransactionMnemonic:
{
CFilePara::MnemonicIteratorDimSize<PARA_TRANSMNEMO_LEN> *mk = new CFilePara::MnemonicIteratorDimSize<PARA_TRANSMNEMO_LEN>(x);
mk->SetRange(&m_Para.ParaTransMnemo[0], &m_Para.ParaTransMnemo[MAX_TRANSM_NO]);
mi = mk;
}
break;
case dwId_ReportMnemonic:
{
CFilePara::MnemonicIteratorDimSize<PARA_REPORTNAME_LEN> *mk = new CFilePara::MnemonicIteratorDimSize<PARA_REPORTNAME_LEN>(x);
mk->SetRange(&m_Para.ParaReportName[0], &m_Para.ParaReportName[MAX_REPO_NO]);
mi = mk;
}
break;
case dwId_SpecialMnemonic:
{
CFilePara::MnemonicIteratorDimSize<PARA_SPEMNEMO_LEN> *mk = new CFilePara::MnemonicIteratorDimSize<PARA_SPEMNEMO_LEN>(x);
mk->SetRange(&m_Para.ParaSpeMnemo[0], &m_Para.ParaSpeMnemo[MAX_SPEM_NO]);
mi = mk;
}
break;
case dwId_LeadThroughMnemonic:
{
CFilePara::MnemonicIteratorDimSize<PARA_LEADTHRU_LEN> *mk = new CFilePara::MnemonicIteratorDimSize<PARA_LEADTHRU_LEN>(x);
mk->SetRange(&m_Para.ParaLeadThru[0], &m_Para.ParaLeadThru[MAX_LEAD_NO]);
mi = mk;
}
break;
}
return mi;
}
使用代理类和迭代器
如下循环所示,使用代理类及其迭代器,用助记符列表填充CListCtrl 对象。我正在使用std::unique_ptr,这样当我不再需要代理类并且std::unique_ptr 超出范围时,内存将被清理。
此源代码的作用是为struct 内的数组创建一个代理对象,该对象对应于指定的助记符标识符。然后,它为该对象创建一个迭代器,使用范围 for 填充 CListCtrl 控件,然后进行清理。这些都是原始的wchar_t 文本字符串,可能正是数组元素的数量,因此我们将字符串复制到临时缓冲区中以确保文本以零结尾。
std::unique_ptr<CFilePara::MnemonicIteratorDimSizeBase> pObj(pFile->MakeIterator(m_IteratorType));
CFilePara::MnemonicIterator pIter(pObj.get()); // provide the raw pointer to the iterator who doesn't own it.
int i = 0; // CListCtrl index for zero based position to insert mnemonic.
for (auto x : pIter)
{
WCHAR szText[32] = { 0 }; // Temporary buffer.
wcsncpy_s(szText, 32, x, pObj->ItemSize());
m_mnemonicList.InsertItem(i, szText); i++;
}