65.9K
CodeProject 正在变化。 阅读更多。
Home

C++ tCNode 模板:使用 STL 容器的索引多节点数据树

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (15投票s)

2014 年 11 月 3 日

CPOL

14分钟阅读

viewsIcon

35129

downloadIcon

778

tCNode 模板:使用 STL 容器的索引多节点数据树

引言

在本文中,我将介绍 tCNode 模板。它是一个模板类,允许程序员在内存中以带索引的多节点数据树的形式组织数据。内部,它使用 map 和 vector 等 STL 容器。它是可移植的。这意味着它可以在 Windows 和 Unix 系统上编译。示例应用程序已在 Windows、Linux 和 MacOS 上进行了测试。

在本文的最后,您将找到 tCNode 模板中所有可用方法的语法摘要和描述。

让我们开始看看如何通过实际案例应用 tCNode 模板。

目录

  1. tCNode 简介
  2. 多线程和同步提示
  3. 不要复制它,引用它
  4. 键、地址和快捷方式
  5. 数据排序器:独立于键对值进行排序
  6. 文章示例解释
  7. tCNode 参考
  8. 结论

1. tCNode 简介

tCNode 模板允许您处理多节点数据树中的数据。每个节点都可以有子节点,这些子节点由一个键索引。

节点的图形表示和主要属性如图 **1** 所示

图 1:tCNode 图形表示

我们直奔主题。以下代码片段展示了使用 tCNode 模板的基础知识。请注意,我是使用 VS2010 编写的。当然,tCNode 可移植到任何 UNIX 发行版(例如 Linux)。

#include "stdafx.h" // REMOVE IF COMPILE UNDER LINUX/UNIX

// INCLUDE THIS!
#include "tcnode.h"

#ifdef _DEBUG           // REMOVE IF COMPILE UNDER LINUX/UNIX
#define new DEBUG_NEW   // REMOVE IF COMPILE UNDER LINUX/UNIX
#endif                  // REMOVE IF COMPILE UNDER LINUX/UNIX

void print_tree(int &_data, std::string &_key, long _deep);

// FIRST, LET US CREATE THE DATA TYPES
//             NAME   DATA  KEY
TNODE_SET_TYPE(Basic, int,  std::string)

// Three new types are created
// TBasic      : tCNode<int, std::string>     
// TBasicRef   : tCNode<int, std::string> &
// TBasicPtr   : tCNode<int, std::string> *
// TBasicNodes : tCNode<int, std::string>::tcnode_subnodes  
int main(int _argc, char* _argv[])
{
   TBasic root;

   root.setDataAndKey(0, "root");
   root.createNode(1, "A");
   root.createNode(3, "C");
   root.createNode(2, "B");

   TBasicRef sub1 = root.createNode(4, "D");
   sub1.createNode(10, "A");
   sub1.createNode(20, "B");

   TBasicRef sub2 = sub1.createNode(30, "C");
   sub2.createNode(100, "A");
   sub2.createNode(200, "B");
   sub2.createNode(300, "C");

   root.createNode(5, "E");
   root.createNode(6, "F");

   root.transverse(print_tree);

   std::cout << "Press enter to continue ..."; 
   std::cin.get(); 

   return  0;
}

void print_tree(int &_data, std::string &_key, long _deep)
{
   int ident =_deep;

   if ( _deep )
      std::cout << "  ";

   while(ident--)
      std::cout << "   ";

   std::cout <<  _key << "=[" << _data << "]\n";
}

编译并运行示例。您将获得如图 **2** 所示的输出。此外,还描述了类型声明和 TNODE_SET_TYPE 的使用。

图 2:tCNode 类型定义
  • 要在 Linux 上运行示例,只需注释掉或删除指示的行,然后使用 g++ 构建可执行文件。
    g++ -o sample sample.cpp
  • 您注意到 transverse 函数了吗?它从 **root** 实例调用。更改该行以
    sub1.transverse(print_tree);
  • 请注意,键如“A”和“B”出现在三个不同的级别。如前所述:键在同一级别内是唯一的。

2. 多线程和同步提示

tCNode 模板不是线程安全的。当 tCNode 实例是共享资源时,您应该创建同步例程以保证数据完整性。这根本不是问题。操作系统提供同步支持 API(Windows 上的 Win32,UNIX 上的 POSIX)。例如,在 Windows 操作系统上

#include "tcnode.h"
using namespace std;

TNODE_SET_TYPE(Node, string, string)

CRITICAL_SECTION CriticalSection; 
TNode allnodes;

int main( void )
{
    ...
}

DWORD WINAPI ThreadProc( LPVOID lpParameter )
{
    ...

    // Request ownership of the critical section.
    EnterCriticalSection(&CriticalSection); 

    allnodes.createNode("John", "Name")

    // Release ownership of the critical section.
    LeaveCriticalSection(&CriticalSection);

    ...
    return 1;
}

3. 不要复制它,引用它

对于大多数 C++ 程序员来说,接下来的几行似乎过于明显,但对于刚接触 C++ 编程的新手,甚至对于学习 C++ 引用的 C 程序员来说,这可能是一个陷阱!

以下代码清晰地展示了 C++ 引用是什么

#include <stdlib.h>
#include <iostream>

using namespace std;

int main(int _argc, char *_argv[])
{

        int X = 10;
        int Y = 99;

        int &A = X; // A became an alias of X

        A = Y; // In fact, X = Y

        cout << "X=" << X << "   Y=" << Y << "\n";

        return 0;
} 

编译并测试它。您将看到 X 和 A 是同一个变量。

现在,让我们看一个完整的示例,它展示了访问特定节点中的数据并对其进行更改的正确方法。当然,“正确方法”取决于您想实现的目标。

在下面的示例中,我们有一个具有 3 个级别的树,其中键是 std::string,数据是 typedef structtCPERSON)。tCPERSON 实例包含基本信息,如全名、年龄和性别。它仅在最后一个级别(级别 2)有意义。级别 0 是根,级别 1 是职业,级别 2 中的节点包含个人信息。

目标是更改 Sarah Neutron 和 Mark Mandarin 这两个人的年龄信息。

    <a name="#T001">// INCLUDE THIS!
#include "tcnode.h"

using namespace std;

typedef struct _CPERSON_
{
   string name;
   int    age;
   string gender;

   _CPERSON_() : name(""), age(0), gender("") { }

   _CPERSON_(string _name, int _age, string _gender) : name(_name), age(_age), gender(_gender) { }

} tCPERSON;

void print_tree(tCPERSON &_data, string &_key, long _deep);

TNODE_SET_TYPE(Person, tCPERSON,  string)

int main(int _argc, char* _argv[])
{
   TPerson rnode;  // deep/level 0

   // DATA DOES NOT MATTER IN THIS LEVEL
   rnode.setDataAndKey(tCPERSON(), "ROOT");
   // deep/level 1
   TPersonRef ref_man = rnode.createNode(tCPERSON(), "MANAGERS"); 

   // deep/level 2
   ref_man.createNode(tCPERSON("John Nobody", 45, "MALE"), "M0001");  
   ref_man.createNode(tCPERSON("Billy Something", 51, "MALE"), "M0002");  
   ref_man.createNode(tCPERSON("Mary Hidden", 38, "FEMALE"), "M0003");  
   
   // deep/level 1
   TPersonRef ref_emp = rnode.createNode(tCPERSON(), "EMPLOYEES");
   
   // deep/level 2
   ref_emp.createNode(tCPERSON("Ed Storm", 28, "MALE"), "E0001");  
   ref_emp.createNode(tCPERSON("Sarah Neutron", 33, "FEMALE"), "E0002");  
   ref_emp.createNode(tCPERSON("Peter Pandora", 38, "MALE"), "E0003");  
   ref_emp.createNode(tCPERSON("Mark Mandarin", 29, "MALE"), "E0004");  
   
   // LET'S PRINT THE TREE
   rnode.transverse(print_tree);

   std::cout << "Press enter to continue ..."; 
   std::cin.get(); 

   // LET'S CHANGE THE AGE OF SARAH NEUTRON
   TPersonPtr person_ptr = rnode.getFirstSubNodeByKey("E0002");
   if ( person_ptr == NULL )
      exit(-1); // OPS! SHOULD NOT HAPPEN

   // LET'S USE REFERENCES!
   TPersonRef person_ref = TNODE_PTR_TO_REF(person_ptr); // POINTER TO REFERENCE
   person_ref.getData().age = 34;

   rnode.transverse(print_tree);

   std::cout << "Press enter to continue ..."; 
   std::cin.get(); 

   // WRONG WAY! UNLESS YOU WANT A COPY
   person_ptr = rnode.getFirstSubNodeByKey("E0004");
   if ( person_ptr == NULL )
      exit(-1); // OPS! SHOULD NOT HAPPEN
   TPerson person_cpy = TNODE_PTR_TO_REF(person_ptr); // THIS IS A COPY!
   person_cpy.getData().age = 44;

   // IN THE ORIGINAL TREE DATA WAS NOT CHANGED
   // person_cpy retains a copy of node returned by getFirstSubNodeByKey
   rnode.transverse(print_tree);

   std::cout << "Press enter to continue ..."; 
   std::cin.get(); 

   return  0;
}

void print_tree(tCPERSON &_data, string &_key, long _deep)
{
   switch(_deep)
   {
      case 0:
         cout << "*** PEOPLE LIST\n";
         break;
      case 1:
         cout << "\t" << _key << ":\n";
         break;
      case 2:
         cout << "\t\tCode  : " << _key << "\n" ;
         cout << "\t\tName  : " << _data.name << "\n";
         cout << "\t\tAge   : " << _data.age << "\n";
         cout << "\t\tGender: " << _data.gender << "\n\n";
         break;
      default:
         break;
   }
}    

见结果

请参阅 **步骤 2**,Sarah 通过引用原始数据,她的年龄确实得到了更改。

 // LET US CHANGE THE AGE OF SARAH NEUTRON
   TPersonPtr person_ptr = rnode.getFirstSubNodeByKey("E0002");
   if ( person_ptr == NULL )
      exit(-1); // OPS! SHOULD NOT HAPPEN

   // LET US USE REFERENCES!
   TPersonRef person_ref = TNODE_PTR_TO_REF(person_ptr); // POINTER TO REFERENCE
   person_ref.getData().age = 34;    

另一方面,Mark 也更改了他的年龄,但更改的是他节点的一个副本!原始数据树没有任何变化。

4. 键、地址和快捷方式

tCNode 数据树中的特定节点可以通过 3 种方式直接访问

回顾上一主题中的示例代码,让我们看看按键访问节点的第一个方法。

以下代码片段展示了 getFirstSubNodeByKey 函数的用法。请注意,它是从根节点调用的,并返回指向第二级节点的指针。我们知道一个键在其级别内是唯一的,但在这种情况下,整个树中只有一个键的值为 E0002。根节点是级别 0,但如果 getFirstSubNodeByKey 是从级别 1 调用,此示例将仍然有效,因为它会从调用它的节点向下遍历树直到最后一个级别。

        // LET'S CHANGE THE AGE OF SARAH NEUTRON
        TPersonPtr person_ptr = rnode.getFirstSubNodeByKey("E0002");
        if ( person_ptr == NULL )
            exit(-1); // OPS! SHOULD NOT HAPPEN     

但是,如果您想指向当前节点的子节点,您将优先选择 getNodeByKey

地址是从根到目标节点的完整路径。它由一个键向量表示。一个例子

  // LET'S CHANGE THE AGE OF SARAH NEUTRON
   TPersonPtr person_ptr = rnode.getFirstSubNodeByKey("E0002");
   if ( person_ptr == NULL )
      exit(-1); // OPS! SHOULD NOT HAPPEN

// GET FULL PATH 
   std::vector<TPerson::tcnode_key> vkeys;
   person_ptr->getNodeFullAddress(vkeys);

   std::cout << "\n\nLEVEL\t KEY\n";

   for ( int x = 0; x < vkeys.size(); x++ )
      std::cout << "  " << x << "\t " << vkeys[x] << "\n";

调用 getNodeFullAddress 后,将返回一个包含构成完整路径的所有键的向量。

在实际情况中,vkeys 可以被保存起来,稍后通过使用 getNodeByFullAddress 来获取同一个节点。

快捷方式是一个 string,允许快速访问特定节点。一个例子

   // LET'S CHANGE THE AGE OF SARAH NEUTRON
   TPersonPtr person_ptr = rnode.getFirstSubNodeByKey("E0002");
   if ( person_ptr == NULL )
      exit(-1); // OPS! SHOULD NOT HAPPEN

   TPersonRef pref = TNODE_PTR_TO_REF(person_ptr);

   std::vector<tperson::tcnode_key> xkeys;
   pref.getNodeFullAddress(xkeys);

   pref.addShortcut("SARAH", xkeys);

   // LATER ...
   TPersonPtr psarah = rnode.getNodeByShortcut("SARAH");
   if ( psarah == NULL )
      exit(-1);

   std::cout << "\nNAME: " << psarah->getData().name << "\n";    

您可以通过调用 addShortcut 并传入一个 string 和节点的完整地址来创建一个快捷方式。请注意,快捷方式与当前节点无关,而是与整个树相关。快捷方式结构保存在根节点中,以便可以独立于应用程序指向的节点来访问。因此,从另一个节点调用 addShortcut 并传入相同的 string 将替换第一个。

5. 数据排序器:独立于键对值进行排序

数据排序器是一种机制,您可以在其中创建规则来对节点中的数据进行排序,而无需考虑键。处理数据排序器操作的函数是 addDataSortergetDataSorterByNameselectDataEqualsTo。您可以创建任意数量的数据排序器。与快捷方式不同,单个节点可以保留自己的数据排序器列表。

让我们看一个完整的示例。

 // INCLUDE THIS!
#include "tcnode.h"

using namespace std;

typedef struct _CPERSON_
{
   string name;
   int    age;
   string gender;

   _CPERSON_() : name(""), age(0), gender("") { }

   _CPERSON_(string _name, int _age, string _gender) : name(_name), age(_age), gender(_gender) { }

} tCPERSON;

TNODE_SET_TYPE(Person, tCPERSON,  string)

// DATA SORTER TO GROUP FEMALES
bool females_sorter(TPersonPtr _ref1, TPersonPtr _ref2)
{
   TPersonRef sub1 = TNODE_PTR_TO_REF(_ref1);
   TPersonRef sub2 = TNODE_PTR_TO_REF(_ref2);

   int res1 =  sub1.getData().gender == "FEMALE" ? 1 : 0;
   int res2 =  sub2.getData().gender == "FEMALE" ? 1 : 0;

   if ( res1 == res2 == 1 )
      return (sub1.getData().name < sub2.getData().name);

   return (res1 > res2);
}

// DATA SORTER TO GROUP MALES IN EMPLOYEES NODE
bool males_employees_sorter(TPersonPtr _ref1, TPersonPtr _ref2)
{
   TPersonRef sub1 = TNODE_PTR_TO_REF(_ref1);
   TPersonRef sub2 = TNODE_PTR_TO_REF(_ref2);

   int res1 =  sub1.getData().gender == "MALE" ? 1 : 0;
   int res2 =  sub2.getData().gender == "MALE" ? 1 : 0;

   if ( res1 == res2 == 1 )
      return (sub1.getData().name < sub2.getData().name);

   return (res1 > res2);
}

// DATA SORTER TO GRUPO AGES FROM OLDER TO YOUNGER
bool ages_sorter(TPersonPtr _ref1, TPersonPtr _ref2)
{
   TPersonRef sub1 = TNODE_PTR_TO_REF(_ref1);
   TPersonRef sub2 = TNODE_PTR_TO_REF(_ref2);

   return (sub1.getData().age > sub2.getData().age);
}

int main(int _argc, char* _argv[])
{
   TPerson rnode;  // deep/level 0

   // DATA DOES NOT MATTER IN THIS LEVEL
   rnode.setDataAndKey(tCPERSON(), "ROOT");
   // deep/level 1
   TPersonRef ref_man = rnode.createNode(tCPERSON(), "MANAGERS"); 

   // deep/level 2
   TPersonRef reftoJohn = ref_man.createNode(tCPERSON("John Nobody", 45, "MALE"), "M0001");  
   std::vector<std::string> keysx;
   reftoJohn.getNodeFullAddress(keysx);
   reftoJohn.addShortcut("POINTER-TO-JOHN", keysx);   

   ref_man.createNode(tCPERSON("Billy Something", 51, "MALE"), "M0002");  
   ref_man.createNode(tCPERSON("Mary Hidden", 38, "FEMALE"), "M0003");  
   ref_man.createNode(tCPERSON("Eva Unah", 38, "FEMALE"), "M0004");  
   
   // deep/level 1
   TPersonRef ref_emp = rnode.createNode(tCPERSON(), "EMPLOYEES");
   
   // deep/level 2
   ref_emp.createNode(tCPERSON("Ed Storm", 28, "MALE"), "E0001");  
   ref_emp.createNode(tCPERSON("Sarah Neutron", 33, "FEMALE"), "E0002");  
   ref_emp.createNode(tCPERSON("Peter Pandora", 38, "MALE"), "E0003");  
   ref_emp.createNode(tCPERSON("Mark Mandarin", 29, "MALE"), "E0004");  

   //  LET'S create females data sorter from root
   rnode.addDataSorter("FEMALES", females_sorter, TRUE);

   // LET'S create males employees data sorter from EMPLOYEES NODE
   TPersonPtr pemployees = rnode.getFirstSubNodeByKey("EMPLOYEES");
   pemployees->addDataSorter("MALE_EMPLOYESS", males_employees_sorter, TRUE);

   // LET'S create AGE data sorter to get all ages from older to younger
   rnode.addDataSorter("AGES", ages_sorter, TRUE);

   // LET'S run data sorter to group/sort data
   // It s usual to run refreshDataSorter after tree has been filled or after any change
   pemployees->refreshDataSorters();
   rnode.refreshDataSorters();

   // SHOW RESULTS
   BOOL bValid = FALSE;
   std::cout << "*** FEMALES LIST (ALL)\n\n"; 
   std::vector<TPersonPtr> &fem = rnode.getDataSorterByName("FEMALES", bValid);
   for ( size_t x = 0; x < fem.size(); x++ )
      if ( fem[x]->getData().gender == "FEMALE" )
         std::cout << fem[x]->getData().name << "\n";

   std::cout << "\n\n*** MALES LIST (EMPLOYEES NODE)\n\n"; 
   pemployees = rnode.getFirstSubNodeByKey("EMPLOYEES");
   std::vector<TPersonPtr> &mal = pemployees->getDataSorterByName("MALE_EMPLOYESS", bValid);
   for ( size_t x = 0; x < mal.size(); x++ )
      if ( mal[x]->getData().gender == "MALE" )
         std::cout << mal[x]->getData().name << "\n";

   std::cout << "\n\n*** AGES FROM OLDER TO YOUNGER\n\n"; 
   std::vector<TPersonPtr> &age = rnode.getDataSorterByName("AGES", bValid);
   for ( size_t x = 0; x < age.size(); x++ )
      if ( age[x]->getData().age > 0 )
         std::cout << age[x]->getData().name << "\t\t" << age[x]->getData().age << " years old\n";

   std::cout << "\n\n";

   system("pause");

   return  0;
}

在示例中,创建了三个数据排序器:第一个用于按性别分组(无论工作如何);第二个用于在 EMPLOYEES 节点中按性别分组男性;最后一个数据排序器按年龄对所有人进行排序。预期输出

6. 文章示例解释

本教程中的演示应用程序展示了 tCNode 的实际用法。名为 dirreader 的演示应用程序的功能正如其名称所示。

给定一个根路径,dirreader 将遍历所有子目录和内容,将每个对象(文件或目录)的数据保存为 tCNode 的节点。语法如下

dirreader <path>   [--print-tree]<path> </path>

您拥有 Windows 和 Unix 两个版本。Unix 版本已在 Linux 和 MacOS 上进行了测试。

例如

dirreader C:\Windows./dirreader /home 将递归读取这些路径中的所有对象,最后将执行 2 个数据排序器:第一个显示最大的 10 个文件,第二个显示最长的 10 个文件名。

--print-tree 选项会绕过数据排序器的执行,并将整个树打印到标准输出。在这种情况下,如果您想记录整个树,您应该使用

./dirreader /home --print-tree >result.txt

Windows 版本是 VS2005 和 VS2010 项目。Unix/Linux 版本可以编译

g++ -o dirreader dirreader.cpp

tcnode.h 是与操作系统无关的相同源代码。事实上,您可以尝试在 Android 或 iOS 项目中使用它。没有理由不起作用!

7. tCNode 参考

本节是对 tCNode 模板类中方法的快速参考。建议您在参考本节之前阅读整篇文章。

构造

回顾 图 2 - tCNode 类型定义,您会清楚地看到您不必使用原始模板声明来创建数据类型。事实上,宏 TNODE_SET_TYPE 是迄今为止最好的方法。

该宏创建了一些您可以在代码中使用的类型。因此,如果您想要一个 DataTree 类型,其中 int 类型作为键,char * 类型作为数据,您应该使用这种方式。

TNODE_SET_TYPE(DataTree, char *, int)

自动创建以下类型

  • TDataTree (tCNode<char *,int>)
  • TDataTreeRef (tCNode<char *,int>::tcnode_ref)
  • TDataTreePtr (tCNode<char *,int>::tcnode_ptr)
  • TDataTreeNodes (tCNode<char *,int>::tcnode_subnodes)
void addDataSorter(_IN std::string _name, _IN _SORTER _receiver, _IN BOOL _recursive = FALSE)

在当前节点创建一个新的数据排序器。数据排序器可以包含它下面的所有树节点(_recursive = TRUE)或仅包含子节点。当调用 refreshDataSorters 时,数据排序器将在当前节点执行。

参数

  • _name [in, required]:用于标识单个数据排序器的 string
  • _receiver [in, required]:一个原型为 bool function(tcnode_ptr _p1, tcnode_ptr _p2) 的排序函数指针
  • _recursive [in, optional]:设置为 TRUE 以包含当前节点下方所有节点和子节点。

返回值

tcnode_ref addShortcut(_IN std::string _label, _IN std::vector<tcnode_key> &_parm)

为树中的一个节点创建一个 string 快捷方式。

参数

  • _label [in, required]:用于标识快捷方式的 string
  • _parm [in, required]:一个包含节点完整地址的向量,由键数组表示

返回值

  • 当前节点的引用。
tcnode_ref createNode(_IN tcnode_data _data, _IN tcnode_key _key)

创建一个由 _key 索引的新子节点。如果节点已存在,则节点数据将被新的 _data 替换。

参数

  • _data [in, required]:在模板声明中定义的数据本身
  • _key [in, required]:在模板声明中定义的键本身

返回值

新子节点或现有节点的引用(如果键已存在)。

std::vector<tcnode_ptr> &getAllSubNodesByKey(_OUT std::vector<tcnode_ptr> &_parm, _IN tcnode_key _key)

从当前节点选择所有与 _key 匹配的子节点。这是递归的。

参数

  • _parm [out, required]:指向匹配 _key 的节点的指针
  • _key [in, required]:用于搜索的键

返回值

  • _parm 将被填充匹配 _key 的节点指针。如果 _key 未找到,_parm 将按原样返回。
long getCount(void)

递归地计算当前节点的所有子节点。

参数

返回值

  • 总子节点数
tcnode_data &getData(void)

获取当前节点中 DATA 的引用。

参数

返回值

  • DATA 的引用
std::vector<tcnode_ptr> &getDataSorterByName(_IN std::string _name, _OUT BOOL &_is_valid)

返回对完整数据排序器数据的引用,即指针的完整数组。必须测试 _is_valid 来知道数据排序器是否有效。

参数

  • _name [in, required]:数据排序器名称
  • _is_valid [out, required]:如果返回的数据排序器有效则为 TRUE,否则为 FALSE

返回值

数据排序器的引用。指向节点的指针数组。TNODE_PTR_TO_REFPTR 转换为 REF

long getDeep(void)

获取当前节点的深度或级别。root 的深度为 0。

参数

返回值

  • 表示当前深度的长整型
tcnode_ptr getFirstSubNodeByKey(_IN tcnode_key _key)

给定一个 KEY 返回一个节点。搜索从当前节点开始,并且是递归的。

参数

  • _key [in, required]: 用于执行搜索的键

返回值

  • 指向子节点的指针(如果找到)。如果未找到则为 NULL。应用程序可以使用 TNODE_PTR_TO_REF 宏将指针转换为引用。
long getId(void)

获取一个唯一标识节点的数字。

参数

返回值

  • 表示节点标识符的长整型
tcnode_key &getKey(void)

获取当前节点中 KEY 的引用。

参数

返回值

  • KEY 的引用
std::vector<tcnode_ptr> &getNextDataSorterInfo(_IN BOOL _begin, _OUT std::string &_name, _OUT BOOL &_recursive, _OUT _SORTER &_sortfunc, _OUT BOOL &_is_valid)

逐个列出节点中的数据排序器。

参数

  • _begin[in, required]TRUE 表示第一个数据排序。FALSE 表示下一个。
  • _name[out, required]:数据排序器名称
  • _recursive[out, required]TRUE 表示数据排序器被设置为递归的
  • _sortFunc[out, required]:由数据排序器运行的排序函数。必须将要传递的变量声明为 T<type name>::SortPredCall
  • _is_valid[out, required]TRUE 表示返回的数据有效。应用程序应测试 _is_valid 以了解数据排序列表何时完成。

返回值

  • 数据排序器数据的引用本身。指向节点的指针数组。TNODE_PTR_TO_REFPTR 转换为 REF
tcnode_ptr getNodeByFullAddress(_IN std::vector<tcnode_key> &_parm)

给定一个地址返回一个节点。请参阅 getNodeFullAddress 来了解如何获取节点地址。

参数

  • _parm [in, required]:表示地址的节点数组

返回值

  • 指向子节点的指针(如果找到)。如果未找到则为 NULL。如果您愿意,可以使用 TNODE_PTR_TO_REF 宏将指针转换为引用。
tcnode_ptr getNodeByKey(_IN tcnode_key _key)

给定一个 KEY 返回子节点。它不是递归的,只搜索子节点级别。递归版本是 getFirstSubNodeByKey

参数

  • _key [in, required]:用于执行搜索的键

返回值

  • 指向子节点的指针(如果找到)。如果未找到则为 NULL。如果您愿意,可以使用 TNODE_PTR_TO_REF 宏将指针转换为引用。
tcnode_ptr getNodeByShortcut(_IN std::string _parm)
  • 给定一个快捷方式名称返回一个节点。

参数

  • _parm [in, required]:快捷方式名称

返回值

指向子节点的指针(如果找到)。如果未找到则为 NULL。如果您愿意,可以使用 TNODE_PTR_TO_REF 宏将指针转换为引用。

std::vector<tcnode_key> &getNodeFullAddress(_OUT std::vector<tcnode_key> &_parm)

获取当前节点的完整地址,由键数组表示。您可以使用返回的数组来创建快捷方式。

参数

  • _parm [out, required]:接收键数组。

返回值

  • _parm 的引用。
tcnode_ref getParent(void)

获取 **父节点** 的引用。

参数

返回值

  • **父节点** 的引用。**根** 返回自身的引用。
tcnode_ref getRoot(void)

获取根节点的引用。

参数

返回值

  • 根节点的引用
tcnode_shortcuts &getShortcuts(void)

获取一个包含所有已定义快捷方式列表的映射。tcnode_shorcuts 类型是

std::map<std::string, std::vector<tcnode_key> >

快捷方式与单个节点无关,而是与整个树相关。因此,您可以从树的任何部分或级别调用此函数。

参数

返回值

  • tcnode_shortcuts 的引用
tcnode_subnodes &getSubNodes(void)

返回当前节点的所有子节点。tcnode_subnodes 是一个 map<key, tCNode>

参数

返回值

  • 包含当前节点子节点的 tcnode_subnodes
BOOL hasSubNodes(void)

如果节点有子节点,则返回 TRUE

参数

返回值

  • 如果节点有子节点则为 TRUE,否则为 FALSE
bool isRoot(void)

如果节点是根节点,则返回 TRUE

参数

返回值

  • 如果是根节点则为 TRUE,否则为 FALSE
void refreshDataSorters(void)

运行当前节点中定义的所有数据排序器。

参数

返回值

bool removeSubNodeByKey(_IN tcnode_key _key)

查找与键匹配的子节点并将其删除。如果应用程序定义了数据排序器,则必须调用 refreshDataSorters 来更新内部引用。

参数

  • _key [in, required]:用于搜索特定节点的键

返回值

  • 如果找到并删除子节点,则为 TRUE。否则为 FALSE
tcnode_ref removeSubNodes(void)

递归删除当前节点的所有子节点。如果应用程序定义了数据排序器,则必须调用 refreshDataSorters 来更新内部引用。

参数

返回值

std::vector<tcnode_ptr> &selectDataEqualsTo(_IN std::string _name, _OUT std::vector<tcnode_ptr> &_parm, _IN const tcnode_data _value)

选择与 _value 匹配的数据排序器节点。您必须传递一个空的 std::vector<tcnode_ptr> 来填充结果。

参数

  • _name [in, required]:用于选择节点的排序器名称
  • _parm [out, required]:填充了结果的数组
  • _value [in, required]:要搜索的值

返回值

_parm 的引用。指向节点的指针数组。TNODE_PTR_TO_REFPTR 转换为 REF

std::vector<tcnode_ptr> &selectDataEqualsTo(_IN std::string _name, _OUT std::vector<tcnode_ptr> &_parm, _IN const std::vector<tcnode_data> &_vals)

选择与 _vals 数组匹配的数据排序器节点。您必须传递一个空的 std::vector<tcnode_ptr> 来填充结果。

参数

  • _name [in, required]:用于选择节点的排序器名称。
  • _parm [out, required]:填充了结果的数组。
  • _vals [in, required]:要搜索的值的数组。

返回值

_parm 的引用。指向节点的指针数组。TNODE_PTR_TO_REFPTR 转换为 REF

tcnode_ref setData(_IN tcnode_data _data)

更改当前节点中的 DATA

参数

  • _data [in, required]:替换当前数据的数据

返回值

  • 当前节点的引用
tcnode_ref setDataAndKey(_IN tcnode_data _data, _IN tcnode_key _key)

更改当前节点中的 DATAKEY

参数

  • _data [in, required]:要替换当前数据的数据
  • _key [in, required]:要替换的当前键

返回值

  • 当前节点的引用
tcnode_ref setKey(_IN tcnode_key _key)

更改当前节点中的 KEY

参数

  • _key [in, required]:要替换的当前键

返回值

  • 当前节点的引用
tcnode_ref setShortcut(_IN std::string _label)

为当前节点设置一个 string 快捷方式。

参数

  • _label [in, required]:用于命名快捷方式的 string

返回值

  • 当前节点的引用
BOOL subNodeExists(_IN tcnode_key _key)

从当前节点开始搜索子节点。搜索是递归的。

参数

  • _key [in, required]:要搜索的键

返回值

  • 如果找到则为 TRUE,否则为 FALSE
template<class _RECV> void transverse(_IN _RECV _receiver)

使用原型为 void function(DATA _data, KEY _key, long _deep) 的回调函数。从当前节点开始,transverse 将为每个子节点调用回调函数,传递 DATAKEYDEEP(或级别)。

参数

  • _receiver [in, required]: 回调函数

返回值

运算符 ==, != 和 =

==!= 用于比较单个节点。使节点相等或不同的原因是内部 id(getId)。**在 tCNode 树中,每个节点都有自己的 Id**。

因此,当应用程序保留对单个节点的多个引用或指针并需要知道该指针或引用是否指向该节点时,这两个运算符才有意义。

复制赋值运算符 (=) 复制所有内容:节点、数据排序器、快捷方式。

7. 结论

我在一些项目中使用过 tCNode 模板,希望它对您有所帮助。我还有其他类,例如用于内存分配的 tMemSection。如果您想了解更多信息,请查看以下文章

本文中的 tCNode 类参考非常简略,因此如果您对如何实现/使用它​​有疑问,可以通过电子邮件向我发送邮件 @ developer@dataaction.com.br,**主题:tCNode 帮助**。

尽情享用!

© . All rights reserved.