C++|以增量开发的方式逐步自定义一个动态数组类

Be here, we are going to write an integer array class from scratch that implements most of the common functionality that containers should have. This array class is going to be a value container, which will hold copies of the elements it’s organizing. As the name suggests, the container will hold an array of integers, similar to std::vector.

在这里,我们将从头开始编写一个整数数组类,该类实现容器应该具有的大多数常见功能。这个数组类将是一个值容器(指存储值而不是指针或引用),它将保存它正在组织的元素的副本。顾名思义,容器将保存一个整数数组,类似于std::vector。

First, let’s create the IntArray.h file:

首先,让我们创建IntArray.h文件:

#ifndef INTARRAY_H#define INTARRAY_Hclass IntArray{};#endif

Our IntArray is going to need to keep track of two values: the data itself, and the size of the array. Because we want our array to be able to change in size, we’ll have to do some dynamic allocation, which means we’ll have to use a pointer to store the data.

我们的IntArray需要跟踪两个值:数据本身和数组的大小。因为我们希望数组能够改变大小,所以必须进行一些动态分配,这意味着必须使用指针来存储数据。

#ifndef INTARRAY_H#define INTARRAY_Hclass IntArray{private: int m_length{}; int* m_data{};};#endif

Now we need to add some constructors that will allow us to create IntArrays. We are going to add two constructors: one that constructs an empty array, and one that will allow us to construct an array of a predetermined size.

现在我们需要添加一些构造函数来创建intarray。我们将添加两个构造函数:一个构造空数组,另一个允许我们构造预定大小的数组。

#ifndef INTARRAY_H#define INTARRAY_H#include // for assert()class IntArray{private: int m_length{}; int* m_data{};public: IntArray() = default; IntArray(int length): m_length{ length } { assert(length >= 0); if (length > 0) m_data = new int[length]{}; }};#endif

We’ll also need some functions to help us clean up IntArrays. First, we’ll write a destructor, which simply deallocates any dynamically allocated data. Second, we’ll write a function called erase(), which will erase the array and set the length to 0.

我们还需要一些函数来帮助我们清理IntArrays。首先,我们将编写一个析构函数,它只需释放任何动态分配的数据。其次,我们将编写一个名为erase()的函数,该函数将擦除数组并将长度设置为0。

~IntArray(){ delete[] m_data; // we don’t need to set m_data to null or m_length to 0 here, since the object will be destroyed immediately after this function anyway}void erase(){ delete[] m_data; // We need to make sure we set m_data to nullptr here, otherwise it will // be left pointing at deallocated memory! m_data = nullptr; m_length = 0;}

Now let’s overload the [] operator so we can access the elements of the array. We should bounds check the index to make sure it’s valid, which is best done using the assert() function. We’ll also add an access function to return the length of the array. Here’s everything so far:

现在让我们重载[]操作符,以便访问数组的元素。我们应该对索引进行边界检查以确保其有效,这最好使用assert()函数来完成。我们还将添加一个访问函数来返回数组的长度。以下是目前为止的所有内容:

#ifndef INTARRAY_H#define INTARRAY_H#include // for assert()class IntArray{private: int m_length{}; int* m_data{};public: IntArray() = default; IntArray(int length): m_length{ length } { assert(length >= 0); if (length > 0) m_data = new int[length]{}; } ~IntArray() { delete[] m_data; // we don’t need to set m_data to null or m_length to 0 here, since the object will be destroyed immediately after this function anyway } void erase() { delete[] m_data; // We need to make sure we set m_data to nullptr here, otherwise it will // be left pointing at deallocated memory! m_data = nullptr; m_length = 0; } int& operator[](int index) { assert(index >= 0 && index < m_length); return m_data[index]; } int getLength() const { return m_length; }};#endif

At this point, we already have an IntArray class that we can use. We can allocate IntArrays of a given size, and we can use the [] operator to retrieve or change the value of the elements.

此时,我们已经有了一个可以使用的IntArray类。我们可以分配给定大小的数组,并且可以使用[]操作符检索或更改元素的值。

However, there are still a few thing we can’t do with our IntArray. We still can’t change its size, still can’t insert or delete elements, and we still can’t sort it.

然而,对于IntArray,仍然有一些事情我们不能做。我们仍然无法更改其大小,仍然无法插入或删除元素,仍然无法对其进行排序。

First, let’s write some code that will allow us to resize an array. We are going to write two different functions to do this. The first function, reallocate(), will destroy any existing elements in the array when it is resized, but it will be fast. The second function, resize(), will keep any existing elements in the array when it is resized, but it will be slow.

首先,让我们编写一些代码来调整数组的大小。我们将编写两个不同的函数来实现这一点。第一个函数reallocation()将在调整数组大小时销毁数组中的所有现有元素,但速度很快。第二个函数resize()将在调整数组大小时保留数组中的所有现有元素,但速度较慢。

// reallocate resizes the array. Any existing elements will be destroyed. This function operates quickly.void reallocate(int newLength){ // First we delete any existing elements erase(); // If our array is going to be empty now, return here if (newLength m_length) ? m_length : newLength }; // Now copy the elements one by one for (int index{ 0 }; index < elementsToCopy; ++index) data[index] = m_data[index]; } // Now we can delete the old array because we don't need it any more delete[] m_data; // And use the new array instead! Note that this simply makes m_data point // to the same address as the new array we dynamically allocated. Because // data was dynamically allocated, it won't be destroyed when it goes out of scope. m_data = data; m_length = newLength;}

Whew! That was a little tricky!

呼!这有点棘手!

Many array container classes would stop here. However, just in case you want to see how insert and delete functionality would be implemented we’ll go ahead and write those too. Both of these algorithms are very similar to resize().

许多数组容器类将在此停止。然而,如果您想了解如何实现插入和删除功能,我们也将继续编写这些内容。这两种算法与resize()非常相似。

void insertBefore(int value, int index){ // Sanity check our index value assert(index >= 0 && index <= m_length); // First create a new array one element larger than the old array int* data{ new int[m_length+1] }; // Copy all of the elements up to the index for (int before{ 0 }; before < index; ++before) data[before] = m_data[before]; // Insert our new element into the new array data[index] = value; // Copy all of the values after the inserted element for (int after{ index }; after = 0 && index < m_length); // If this is the last remaining element in the array, set the array to empty and bail out if (m_length == 1) { erase(); return; } // First create a new array one element smaller than the old array int* data{ new int[m_length-1] }; // Copy all of the elements up to the index for (int before{ 0 }; before < index; ++before) data[before] = m_data[before]; // Copy all of the values after the removed element for (int after{ index+1 }; after < m_length; ++after) data[after-1] = m_data[after]; // Finally, delete the old array, and use the new array instead delete[] m_data; m_data = data; –m_length;}// A couple of additional functions just for conveniencevoid insertAtBeginning(int value) { insertBefore(value, 0); }void insertAtEnd(int value) { insertBefore(value, m_length); }

If we want to initialize a array with values, we can do so directly via the initializer list syntax.

如果要使用值初始化数组,可以直接通过初始化列表语法进行初始化。

The IntArray class also can have a constructor with an initializer list.

IntArray类还可以具有具有初始化列表的构造函数。

As a result, std::initializer_list can be used to implement constructors, assignment operators, and other functions that accept a list initialization parameter. std::initailizer_list lives in the header.

因此,std::initializer_list可用于实现构造函数、赋值运算符和其他接受列表初始化参数的函数。std::initializer_list位于头文件中。

IntArray(std::initializer_list list) // allow IntArray to be initialized via list initialization: IntArray(static_cast(list.size())) // use delegating constructor to set up initial array{// Now initialize our array from the listint count{ 0 };for (auto element : list){m_data[count] = element;++count;}}

Here is our IntArray container class in its entirety.

这是我们的IntArray容器类的全部内容。

IntArray.h:

#ifndef INTARRAY_H#define INTARRAY_H#include // for assert()class IntArray{private: int m_length{}; int* m_data{};public: IntArray() = default; IntArray(int length): m_length{ length } { assert(length >= 0); if (length > 0) m_data = new int[length]{}; }IntArray(std::initializer_list list) // allow IntArray to be initialized via list initialization: IntArray(static_cast(list.size())) // use delegating constructor to set up initial array{// Now initialize our array from the listint count{ 0 };for (auto element : list){m_data[count] = element;++count;}} ~IntArray() { delete[] m_data; // we don’t need to set m_data to null or m_length to 0 here, since the object will be destroyed immediately after this function anyway } void erase() { delete[] m_data; // We need to make sure we set m_data to nullptr here, otherwise it will // be left pointing at deallocated memory! m_data = nullptr; m_length = 0; } int& operator[](int index) { assert(index >= 0 && index < m_length); return m_data[index]; } // reallocate resizes the array. Any existing elements will be destroyed. This function operates quickly. void reallocate(int newLength) { // First we delete any existing elements erase(); // If our array is going to be empty now, return here if (newLength m_length) ? m_length : newLength }; // Now copy the elements one by one for (int index{ 0 }; index = 0 && index <= m_length); // First create a new array one element larger than the old array int* data{ new int[m_length+1] }; // Copy all of the elements up to the index for (int before{ 0 }; before < index; ++before) data[before] = m_data[before]; // Insert our new element into the new array data[index] = value; // Copy all of the values after the inserted element for (int after{ index }; after = 0 && index < m_length); // If we're removing the last element in the array, we can just erase the array and return early if (m_length == 1) { erase(); return; } // First create a new array one element smaller than the old array int* data{ new int[m_length-1] }; // Copy all of the elements up to the index for (int before{ 0 }; before < index; ++before) data[before] = m_data[before]; // Copy all of the values after the removed element for (int after{ index+1 }; after < m_length; ++after) data[after-1] = m_data[after]; // Finally, delete the old array, and use the new array instead delete[] m_data; m_data = data; –m_length; } // A couple of additional functions just for convenience void insertAtBeginning(int value) { insertBefore(value, 0); } void insertAtEnd(int value) { insertBefore(value, m_length); } int getLength() const { return m_length; }};#endif

Now, let’s test it just to prove it works:

现在,让我们测试一下,以证明它是有效的:

#include #include “IntArray.h”int main(){ // Declare an array with 10 elements IntArray array(10); // Fill the array with numbers 1 through 10 for (int i{ 0 }; i<10; ++i) array[i] = i+1; // Resize the array to 8 elements array.resize(8); // Insert the number 20 before element with index 5 array.insertBefore(20, 5); // Remove the element with index 3 array.remove(3); // Add 30 and 40 to the end and beginning array.insertAtEnd(30); array.insertAtBeginning(40); // Print out all the numbers for (int i{ 0 }; i

This produces the result:

40 1 2 3 5 20 6 7 8 30

Although writing container classes can be pretty complex, the good news is that you only have to write them once. Once the container class is working, you can use and reuse it as often as you like without any additional programming effort required.

尽管编写容器类可能相当复杂,但好消息是您只需编写一次。一旦容器类开始工作,您就可以随时使用和重用它,而无需进行任何额外的编程工作。

It is also worth explicitly mentioning that even though our sample IntArray container class holds a built-in data type (int), we could have just as easily used a user-defined type (e.g. a Point class).

还值得一提的是,即使我们的示例IntArray容器类包含内置数据类型(int),我们也可以同样轻松地使用用户定义的类型(例如Point类)。

One more thing: If a class in the standard library meets your needs, use that instead of creating your own. For example, instead of using IntArray, you’re better off using std::vector. It’s battle tested, efficient, and plays nicely with the other classes in the standard library. But sometimes you need a specialized container class that doesn’t exist in the standard library, so it’s good to know how to create your own when you need to. We’ll talk more about containers in the standard library once we’ve covered a few more fundamental topics.

还有一件事:如果标准库中的类满足您的需要,请使用它,而不是创建自己的类。例如,与其使用IntArray,不如使用std::vector。它经过了严格测试,效率很高,并且与标准库中的其他类配合得很好。但有时您需要一个标准库中不存在的专用容器类,因此最好知道如何在需要时创建自己的容器类。一旦我们涵盖了一些更基本的主题,我们将进一步讨论标准库中的容器。

ref

16.6 — Container classes

-End-

郑重声明:本文内容及图片均整理自互联网,不代表本站立场,版权归原作者所有,如有侵权请联系管理员(admin#wlmqw.com)删除。
(0)
用户投稿
上一篇 2022年6月16日
下一篇 2022年6月16日

相关推荐

  • 机器学习帮助拯救受威胁的物种免遭灭绝

    地球上有数以千计的物种我们仍然不了解。但我们现在知道,它们已经在灭绝的边缘徘徊。一项新的研究使用机器学习来计算这些鲜为人知的物种受到了多大的威胁,其结果是严峻的。 一些动物和植物物…

    2022年8月17日
  • 原神:留好“蓝球”!提纳里要进常驻池了,策划为大家省钱

    派蒙:一切以最终公布为准喔,目前吃瓜先~ 冻梨:不过囤好蓝球等一波也不亏就是了! 消息来源层岩舅,一位多次透露消息,可信度较高的爆料者:“虽然很喜欢,但是还是放弃了,等歪”。一句话…

    2022年7月30日
  • 保楼市还是保生育?为鼓励生育,国家向住房出手了

    生育率连年创新低 国家统计局数据显示,2017-2021年,我国出生人口出现“5连跌”。数据显示,2021年出生人口只有1062万人,创60年来新低,出生率0.75%,连续2年跌破…

    2022年8月5日
  • 善待自己的最好方式,你一定要知道

    大家好 ,“麦田里 的 晚 风 ” 第 222篇 文章 ,记得 点赞 与 关注 ,不断 为你 分享 生活 哲理 与 乐趣 。 人生总有太多的不如意,人总会遇到太多的是非,在悄无声息…

    2022年7月9日
  • 标致408“换标”富康复活!卖15.38万,PK秦PLUS,你会买单吗?

    富康ES600对标秦PLUS EV必败·没有悬念提到富康汽车相信老司机都会比较熟悉,早期的雪铁龙富康也算是“老三样”之一了,和桑塔纳捷达是齐名的车;不过富康早早的停产了,桑塔纳和捷…

    2022年7月31日
  • 《英雄联盟手游》3.1版本今日更新:更像PC端游了-

    3月24日上午10:00,《英雄联盟手游》全新3.1版本将正式更新,召唤师需要更新至最新版本才能进入游戏。 在即将到来的3.1版本“艾欧尼亚守护者”中,峡谷将加入不少刺激好玩的新功…

    2022年6月30日
  • OC底层原理(二).内存分配与内存对齐

    从内存分配开始 在上一篇的流程图中,我们看到最后的流程中,在_class_createInstanceFromZone,我们分为三步: 1、size = cls->insta…

    2022年7月3日
  • 基本粒子和相对原子质量

    基本粒子泛指比原子核小的物质单元,包括电子,中子,质子,光子,以及在宇宙射线和高能原子核实验中发现的一系列粒子。 目前已经发现的基本粒子有30多种,按质量大小和性质上的差异,可以分…

    2022年8月26日
  • 哪吒汽车:7月交付1.4万台,同比增133.5%

    36氪获悉,哪吒汽车公布7月销量数据,7月哪吒V和哪吒U共交付14037台,同比增长133.5%;1-7月交付77168台,同比增长184.6%。

    2022年8月7日
  • 53岁邓文迪罕见晒美照!穿“鸡毛掸”粉裙又扮嫩,却显得一脸凶相

    MetGala虽然已经结束了,但是明星名媛们还都沉浸在自己在红毯上的闪耀表现。已经很久都没有露面邓文迪,这次也是罕见亮相,而且时隔半年,邓文迪终于更新了自己的社交平台,晒出了两张自…

    2022年5月11日

联系我们

联系邮箱:admin#wlmqw.com
工作时间:周一至周五,10:30-18:30,节假日休息