Delphi实现开源Vector向量容器类
<p><span>几周前,我</span>在C ++ Builder上写了一篇文章,在<span>那里我完全超出了我的舒适范围,并实现了我的第一个C ++类。</span></p><p><span><span>自从我对C ++ Builder产生了兴趣之后,在接下来的几周和几个月中,我将继续使用C / C ++进行冒险,但是与其他许多人一样,Corona病毒迫使我对工作进行不同的优先级排序。</span><span>所以暂时我的焦点将是Delphi。</span></span></p>
<p><span><span>在这篇Delphi文章中,我想谈谈Vector向量容器,这是C ++中的一个概念,对于Delphi开发人员可能是陌生的。</span><span>当您阅读“Vector向量”一词时,您可能会想到3D图形和矩阵数学。</span><span>但是向量容器完全是另外一回事。</span></span></p>
<p><span><span>简而言之,向量容器是一个基于泛型的类,可简化您使用可表示为“值序列”的列表,数组,队列和数据的方式。</span><span>这听起来可能不太令人兴奋,但请继续阅读,您可能会感到惊讶。</span></span></p>
<p><span><span>我必须承认,“载体”一词最初对于这样一个基本而扎根的话题似乎显得格格不入;</span><span>但是,如果您用Google搜索该词的含义,则</span></span><em>矢量</em>仅表示<em>方向</em>或<em>标题</em><span><span>。</span><span>从所有手段和目的来看,指针都是一个标题。</span></span></p>
<h1>Delphi中的Vector向量</h1>
<p><span><span>尽管在功能方面,Object Pascal和C ++在各方面实际上都是相同的,但是在某些方面,语言和生态系统是不同的;</span><span>从根本上讲,在某些地方。</span><span>Delphi可能没有与C ++标准库完全相同的报告,但是它始终为我们提供了实现相同目标的良好选择。</span><span>Delphi和C ++ Builder在对VCL和FMX框架的支持上有共同点,但是vector类不是VCL或FMX的一部分,而是C ++标准库(std :: vector)。</span><span>因此,Delphi实际上并没有直接匹配的东西。</span></span></p>
<p><span><span>因为我发现Vector向量容器在C ++中是如此有用,所以我想了-为什么不为Delphi创建Vector向量实现?</span><span>而这正是我所做的。</span><span>因此,在本文中,我将引导您了解Vector向量列表为何有用,如何使用它们-最后但并非最不重要的一点是,您可以在自己的项目中复制和使用的完整源代码(完全免费和开源,没有字符串)附件)。</span></span></p>
<p><strong><span><span>注意</span></span></strong><span><span>:本文结尾处提供了BitBucket存储库链接。</span></span></p>
<h2>集装箱,有什么大不了的?</h2>
<p><span><span>如前所述,Vector向量类只是一个容器;</span><span>处理“数据序列”的容器,在大多数情况下,这表示列表或数组。</span><span>向量类之所以被称为容器,是因为它不涉及项目的分配或存储方式,而是将其留给了另一个称为分配器的类。</span></span></p>
<p><span><span>考虑向量类(纯粹作为协同作用或并行作用)的一种有用方法是考虑TDataset。</span><span>TDataset可以抽象出数据的来源以及其在幕后的组织方式。</span><span>就像TDataset一样,向量类提供了导航数据序列的方法,但是它自身并不包含细节。</span></span></p>
<p><span><span>由于向量列表实际上在C ++生态系统中的每个地方都被使用,因此它有助于减少您需要编写的代码量。</span><span>在数组上使用的相同代码将适用于列表。</span><span>开发人员还可以编写自己的分配器实例,将数据存储在磁盘上或将数据库记录直接映射到C结构。</span><span>只要有人为该特定方案实现了分配器,向量容器就可以表示它。</span><span>能够自己滚动,会带来一些有趣的可能性。</span></span></p>
<p> </p>
<h2><span>分配器类</span></h2>
<p><span><span>C ++标准库开箱即用地提供了两个分配器类,这两个分配器类均表示可与类型化数据一起使用的内存中列表。</span><span>还有上面我提到的第三个分配器类型(或从技术上讲,分配器继承自的基类),这意味着您可以提供自己的类型。</span><span>总结一下:</span></span></p>
<ul>
<li><span>已排序</span></li>
<li><span>动态</span></li>
<li><span>自订</span></li>
</ul>
<p><span>让我们看看它们中的每一个,看看有什么区别。</span></p>
<h3><span>顺序分配器</span></h3>
<p><span><span>序列化意味着您的项目列表被分配为一个连续的内存块。</span><span>一个物品一个接一个地整齐地排列。</span><span>在Delphi中最接近的东西是打包记录的数组。</span><span>该分配器的优点是读取值异常快,因为它可以使用简单的数学计算每个项目的偏移量。</span></span></p>
<p><span><span>缺点是,在填充这样的向量列表时,必须非常频繁地重新分配内存。</span><span>在我的谦虚实现中,我没有花时间实现缓存机制,这意味着每次添加,插入或删除项目时,都会重新分配保存该列表的整个内存段(我将在下一个修订版中添加一些内容) )。</span></span></p>
<p><span><span>但是,C ++规范在适当的缓存下运行,分配的项目比实际使用的多,因此将重新分配的数量降至最低。</span><span>对于Delphi列表,相同的容量和使用系统也是常见的。</span></span></p>
<p><span><span>顺序分配器的另一个好处是,整个内容可以快速转储到磁盘或在内存中复制。</span><span>毕竟所有项目都保存在单个内存中,从而可以预测大小和偏移量(因此您可以在单个操作中将数据写入TFileStream)。</span><span>两个向量列表(具有相同分配器类型)之间的分配同样有效。</span></span></p>
<p><span>因此,尽管普通的编程任务可能不会立即从顺序存储中受益,但在某些低层场景中,Vector向量容器可以创造奇迹。</span></p>
<p><span><span>出于好奇,我可以提到在Nintendo DS等平台上,显示内容实际上是一个文件。</span><span>因此,通过将值写入文件系统上的文件来绘制图形。</span><span>C ++开发人员通过将Vector容器与自定义分配器结合使用,可以将文件映射到内存中,从而非常优雅地解决了这一罕见而难以理解的障碍。</span><span>这样,像素可以像数组一样进行读写。</span></span></p>
<p><strong>注意</strong><span>:</span><em><span><span>我并不是说Delphi可以针对Nintendo DS。</span><span>我只是指出面对异常数据表示时向量容器的灵活性和强大性。</span></span></em></p>
<h3><span>动态分配器</span></h3>
<p><span>动态与普通Delphi数组存储信息的方式大致相同。</span></p>
<p><span><span>这种分配器类型没有关于如何将项目存储在内存中的标准。</span><span>只要分配器可以交付每个项目,向量容器就不会在乎细节。</span></span></p>
<p><span>这与排序模型的正好相反,排序模型要求以可预测的方式存储项目。</span></p>
<p><span>在Delphi中,最接近动态分配器的匹配项是常规TArray <>。</span></p>
<h3><span>定制分配器</span></h3>
<p><span><span>如果向量容器仅使用顺序或动态分配器进行操作,则与普通列表或集合相比,它们的用处将微不足道。</span><span>向量容器的强大之处在于它们允许任何人实现自己的分配器,从而可以处理几乎任何来源或介质中的数据序列,而不会破坏接口。</span><span>这是节省时间的功能所在。</span></span></p>
<p><span><span>如果某些东西可以表示为值的列表或序列,则可以为其编写分配器;</span><span>因此-可以通过公共向量接口访问数据。</span></span></p>
<p><span><span>这是一些可以实现的分配器建议。</span><span>最终,每个开发人员都要编写一个适合自己特定情况的分配器类:</span></span></p>
<ul>
<li><span>将数据库记录映射到Pascal记录的分配器</span></li>
<li><span>与基于磁盘的阵列一起运行的分配器</span></li>
<li><span>与JSON或XML节点一起使用的分配器</span></li>
<li><span>使用内存映射的分配器</span></li>
<li><span>包装目录列表的分配器</span></li>
<li><span>包装文本文件的分配器</span></li>
</ul>
<p><span>不用说,一旦您将Delphi强大的RTTI添加到方程式中,事情就会变得很有趣。将颈椎枕数据库字段映射到记录的向量列表将很容易创建。就ORM(对象关系映射)而言,避免类实例并直接映射到Pascal记录类型将更快,内存效率更高。因此,此材料中有一些未开发且引人入胜的机会。</span></p>
<h2><span>一个实际的例子</span></h2>
<p><span><span>在查看实际的矢量类实现之前,让我们看一下如何使用它。</span><span>由于顺序分配器非常适合在单个内存块中维护记录(在处理低级任务时可能非常强大),让我们开始吧。</span></span></p>
<p> </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">type</span><span style="color: rgba(0, 0, 0, 1)">
TTest </span>= <span style="color: rgba(0, 0, 255, 1)">record</span><span style="color: rgba(0, 0, 0, 1)">
id: integer;
name: shortstring;
</span><span style="color: rgba(0, 0, 255, 1)">class</span> <span style="color: rgba(0, 0, 255, 1)">function</span> <span style="color: rgba(0, 0, 255, 1)">Create</span><span style="color: rgba(0, 0, 0, 1)">(id: integer; name: shortstring): TTest; static;
</span><span style="color: rgba(0, 0, 255, 1)">end</span><span style="color: rgba(0, 0, 0, 1)">;
TMyVectorList </span>= <span style="color: rgba(0, 0, 255, 1)">class</span>(TVector<TTest><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">end</span>;</pre>
</div>
<p> </p>
<p><span><span>在上面的代码中,我们定义了一个带有几个字段的简单记录结构。</span><span>为了使生活更轻松,我们还定义了一个函数来初始化记录,这样我们就不会在源代码中添加太多样板代码。</span><span>helper函数的实现很简单:</span></span></p>
<p> </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span> <span style="color: rgba(0, 0, 255, 1)">function</span> TTest.<span style="color: rgba(0, 0, 255, 1)">Create</span><span style="color: rgba(0, 0, 0, 1)">(id: integer; name: shortstring): TTest;
</span><span style="color: rgba(0, 0, 255, 1)">begin</span><span style="color: rgba(0, 0, 0, 1)">
result.id :</span>=<span style="color: rgba(0, 0, 0, 1)"> id;
result.name :</span>=<span style="color: rgba(0, 0, 0, 1)"> name;
</span><span style="color: rgba(0, 0, 255, 1)">end</span>;</pre>
</div>
<p> </p>
<p><span><span>您会注意到,我们还定义了一个类TMyVectorList,该类仅从TVector继承为我们的记录类型。</span><span>我们真的不需要这样做。</span><span>您可以只创建TVector <TTest>的实例,但是如果您想扩展带有某些实用程序功能的矢量容器,则将其隔离可以使代码更易于阅读和使用。</span></span></p>
<p><span><span>接下来,切换到窗体设计器,并在窗体上放置一个TButton和一个TListbox。</span><span>尽管对于我们的示例而言并不重要,但您可能需要将列表框的锚点设置为左,上,右和下,以使其随窗体调整大小。</span></span></p>
<p><span><span><img src="https://img2020.cnblogs.com/blog/1969063/202004/1969063-20200416102003115-2079477446.png"></span></span></p>
<p> </p>
<p> 完成表单设计后,双击按钮并填写以下代码:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">procedure</span><span style="color: rgba(0, 0, 0, 1)"> TForm1.Button1Click(Sender: TObject);
</span><span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)">
x: integer;
lList: TMyVectorList;
</span><span style="color: rgba(0, 0, 255, 1)">begin</span><span style="color: rgba(0, 0, 0, 1)">
lList :</span>= TMyVectorList.<span style="color: rgba(0, 0, 255, 1)">Create</span><span style="color: rgba(0, 0, 0, 1)">(vaSequence);
</span><span style="color: rgba(0, 0, 255, 1)">try</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> populate</span>
<span style="color: rgba(0, 0, 255, 1)">for</span> x := <span style="color: rgba(128, 0, 128, 1)">1</span> <span style="color: rgba(0, 0, 255, 1)">to</span> <span style="color: rgba(128, 0, 128, 1)">10</span> <span style="color: rgba(0, 0, 255, 1)">do</span>
<span style="color: rgba(0, 0, 255, 1)">begin</span><span style="color: rgba(0, 0, 0, 1)">
lList.add( TTest.</span><span style="color: rgba(0, 0, 255, 1)">Create</span>(x, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">name #</span><span style="color: rgba(128, 0, 0, 1)">'</span> + IntToStr(x-<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">) ) );
</span><span style="color: rgba(0, 0, 255, 1)">end</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> do some inserts</span>
lList.insert(<span style="color: rgba(128, 0, 128, 1)">4</span>, TTest.<span style="color: rgba(0, 0, 255, 1)">Create</span>(<span style="color: rgba(128, 0, 128, 1)">4</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">first</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">) );
lList.insert(</span><span style="color: rgba(128, 0, 128, 1)">9</span>, TTest.<span style="color: rgba(0, 0, 255, 1)">Create</span>(<span style="color: rgba(128, 0, 128, 1)">9</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">second</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">) );
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Read back</span>
<span style="color: rgba(0, 0, 255, 1)">for</span> x := <span style="color: rgba(128, 0, 128, 1)">0</span> <span style="color: rgba(0, 0, 255, 1)">to</span> lList.count -<span style="color: rgba(128, 0, 128, 1)">1</span> <span style="color: rgba(0, 0, 255, 1)">do</span>
<span style="color: rgba(0, 0, 255, 1)">begin</span><span style="color: rgba(0, 0, 0, 1)">
listbox1.items.add( lList.Items.name );
</span><span style="color: rgba(0, 0, 255, 1)">end</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)">
lList.Clear;
lList.</span><span style="color: rgba(0, 0, 255, 1)">free</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">end</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">end</span>; </pre>
</div>
<p> 按下F9键(编译并运行),然后单击填充按钮,结果应为:</p>
<p><img src="https://img2020.cnblogs.com/blog/1969063/202004/1969063-20200416102059192-1895817729.png"></p>
<h2><span>有什么好处?</span></h2>
<p><span><span>如果您想知道为什么上述内容与使用标准TArray或TList实现相同之处有什么不同,则必须记住分配器的工作方式。</span><span>这里的区别不是它做什么,而是如何做。</span><span>您必须考虑到,向量列表的功能不是容纳记录列表的能力,而是它提供了一个统一的接口来按顺序访问值-不论其来源或格式。</span></span></p>
<p><span><span>TVector不了解列表数据的来源或来源。</span><span>它只知道通用类型(在我们的示例中为TTest)和用于管理列表的分配器。</span><span>除此之外,TVector只是一个统一的访问点。</span></span></p>
<p><span>在上面的示例中,我们使用了顺序分配器,因此列表项存储在单个内存块中。</span></p>
<p><span><span>为确保内存完整性一致,让我们仔细检查!</span><span>让我们使用指针直接访问旧式的内存!</span></span></p>
<h2><span>访问原始数据</span></h2>
<p><span><span>在我们的下一个示例中,我们将访问向量包含的原始内存(或更准确地说,分配器提供的向量公开)。</span><span>我谦虚的vector实现使容器和分配器方便地解耦,仅受接口引用约束。</span><span>但是该接口公开了一些方法,这些方法使我们可以利用数据(当然适用时)。</span></span></p>
<p><span>有问题的方法是:</span></p>
<ul>
<li><span>LockMemory(可变数据)</span></li>
<li><span>UnlockMemory()</span></li>
<li><span>GetDataSize()</span></li>
</ul>
<p><span><span>我要强调的是,在实现自己的向量分配器类时,如果无法或不允许进行内存访问,则应引发异常。</span><span>我非常不愿意完全包含LockMemory()和UnLockMemory(),因为容器的全部目的是抽象化内容的知识。</span><span>但是,实话实说,所有这一切都需要实践。</span></span></p>
<p><span>因此,切换回表单设计器,让我们添加另一个按钮,如下所示:</span></p>
<p><span> <img src="https://img2020.cnblogs.com/blog/1969063/202004/1969063-20200416102141142-1160981064.png"></span></p>
<p> 接下来,我们定义记录类型的指针变体:</p>
<div class="cnblogs_code">
<pre>PTest =<span style="color: rgba(0, 0, 0, 1)"> ^TTest;
TTest </span>= <span style="color: rgba(0, 0, 255, 1)">record</span><span style="color: rgba(0, 0, 0, 1)">
id: integer;
name: shortstring;
</span><span style="color: rgba(0, 0, 255, 1)">class</span> <span style="color: rgba(0, 0, 255, 1)">function</span> <span style="color: rgba(0, 0, 255, 1)">Create</span><span style="color: rgba(0, 0, 0, 1)">(id: integer; name: shortstring): TTest; static;
</span><span style="color: rgba(0, 0, 255, 1)">end</span>;</pre>
</div>
<p> </p>
<p> <span>定义了记录的指针类型后,我们可以直接从内存地址访问记录。</span><span>现在,双击新按钮并填写以下代码:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">procedure</span><span style="color: rgba(0, 0, 0, 1)"> TForm1.Button2Click(Sender: TObject);
</span><span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)">
x: integer;
lList: TMyVectorList;
lRaw: PTest;
</span><span style="color: rgba(0, 0, 255, 1)">begin</span><span style="color: rgba(0, 0, 0, 1)">
lList :</span>= TMyVectorList.<span style="color: rgba(0, 0, 255, 1)">Create</span><span style="color: rgba(0, 0, 0, 1)">(vaSequence);
</span><span style="color: rgba(0, 0, 255, 1)">try</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> populate</span>
<span style="color: rgba(0, 0, 255, 1)">for</span> x := <span style="color: rgba(128, 0, 128, 1)">1</span> <span style="color: rgba(0, 0, 255, 1)">to</span> <span style="color: rgba(128, 0, 128, 1)">10</span> <span style="color: rgba(0, 0, 255, 1)">do</span>
<span style="color: rgba(0, 0, 255, 1)">begin</span><span style="color: rgba(0, 0, 0, 1)">
lList.add( TTest.</span><span style="color: rgba(0, 0, 255, 1)">Create</span>(x, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">name #</span><span style="color: rgba(128, 0, 0, 1)">'</span> + IntToStr(x-<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">) ) );
</span><span style="color: rgba(0, 0, 255, 1)">end</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Get a pointer to the managed memory</span>
lRaw := <span style="color: rgba(0, 0, 255, 1)">nil</span><span style="color: rgba(0, 0, 0, 1)">;
llist.Memory.LockMemory(lRaw);
</span><span style="color: rgba(0, 0, 255, 1)">try</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> manually read out the name field from each</span>
<span style="color: rgba(0, 0, 255, 1)">for</span> x := <span style="color: rgba(128, 0, 128, 1)">0</span> <span style="color: rgba(0, 0, 255, 1)">to</span> lList.Count-<span style="color: rgba(128, 0, 128, 1)">1</span> <span style="color: rgba(0, 0, 255, 1)">do</span>
<span style="color: rgba(0, 0, 255, 1)">begin</span><span style="color: rgba(0, 0, 0, 1)">
listbox1.items.add( lRaw^.Name );
inc(lRaw);
</span><span style="color: rgba(0, 0, 255, 1)">end</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)">
lList.Memory.UnLockMemory;
</span><span style="color: rgba(0, 0, 255, 1)">end</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)">
lList.Clear;
lList.</span><span style="color: rgba(0, 0, 255, 1)">free</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">end</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">end</span>; </pre>
</div>
<p> 按下F9键(编译并运行),单击“新建”按钮,您应该看到以下结果:</p>
<p><img src="https://img2020.cnblogs.com/blog/1969063/202004/1969063-20200416102300801-979745844.png"></p>
<p>如您所见,顺序分配器可保持数据完整性并提供可预测的结果。</p>
<h2><span>无数的支持</span></h2>
<p><span><span>原始的C ++向量标准类开箱即用地支持枚举器,但是C ++标准库中定义枚举器的方式与Delphi不兼容。</span><span>因此,我没有实现与Delphi不兼容的枚举器模式,而是自然选择了传统的Delphi枚举器系统。</span><span>与C ++向量列表100%兼容会很酷,但是在兼容和可用之间有一条细线。</span><span>我总是站在实践的一边。</span></span></p>
<p><span>因此,TVector类继承自TEnumerable,因此您可以方便,现代的方式访问列表,例如:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> el: TTest;
</span><span style="color: rgba(0, 0, 255, 1)">for</span> el <span style="color: rgba(0, 0, 255, 1)">in</span> lVectorList <span style="color: rgba(0, 0, 255, 1)">do</span>
<span style="color: rgba(0, 0, 255, 1)">begin</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> do something with el</span>
<span style="color: rgba(0, 0, 255, 1)">end</span>;</pre>
</div>
<p> </p>
<h2><span>坚持不懈</span></h2>
<p><span><span>使用从TEnumerable继承的向量列表,您可能想知道为什么我要舍弃TPersistence的好处而转向枚举器?</span><span>向量列表实现了标准的Assign()方法,因此您可以在向量容器之间分配内容。</span><span>它还将检查分配器模型是否是顺序的-并在可能的情况下快速调整大小并移动整个内存块。</span></span></p>
<p><span>我还确保分配器基类继承自泰山颈椎枕TInterfacedPersistent,因此以后在此方面还有很大的改进空间。读者,我将把这一部分留给您。</span></p>
<h2><span>Delphi Vector向量类单元</span></h2>
<p><span><span>我认为到目前为止,我们已经涵盖了足够多的理论和用例,因此现在该检查代码了。</span><span>从字面上看。</span></span></p>
<p><span><span>由于代码覆盖了20个A4页面,因此我自由地在BitBucket上建立了一个存储库。</span><span>该代码是根据MPL v2(Mozilla公共许可证)发布的,因此您可以在自己的项目中安全地使用它。</span></span></p>
<p><span>您可以在此处下载或存储库:</span></p>
<p>https://bitbucket.org/cipher_diaz/pasvector/src/master/</p>
<p><span><span>该存储库包含一个Delphi和Freepascal示例。</span><span>虽然在面向Delphi的生态系统中包含Freepascal似乎很奇怪,但该代码是为同时使用TMS Web Framework(使用Freepascal将Delphi代码转换为JavaScript)的Delphi开发人员提供的。</span></span></p>
<p>全屏</p><br><br>
来源:https://www.cnblogs.com/Z-SHi/p/12710871.html 看到LZ分享的这篇关于Delphi Vector向量容器类的文章,真的很有收获!作为一个经常在Delphi和C++之间切换的开发者,这个话题让我很有共鸣。
首先得说,您这个实现很有价值!虽然Delphi有TList、TArray这些现成的容器,但它们在使用灵活性和泛型支持上确实不如C++的std::vector。
关于分配器的设计,我觉得特别有意思:
- 顺序分配器适合需要直接操作内存的场景
- 动态分配器更灵活
- 自定义分配器才是真正的亮点,可以对接数据库、文件、内存映射等各种数据源
有个小建议:您提到没有实现缓存机制,其实在实际使用中,预先分配 Capacity 确实能大幅提升性能。这点对于大数据量处理很重要。
另外,看到您提到可以用Vector做ORM相关的功能,这个思路很棒!用记录直接映射数据库字段,确实比用类实例更节省内存,执行效率也更高。
已收藏您的BitBucket仓库链接,有空一定会好好研究一下代码。期待您后续的更新!
顺便一问:您有没有考虑过给这个项目加上单元测试?这样更方便其他开发者放心使用。
頁:
[1]