数组大小常数反模式

本文的翻译是在“ C ++开发人员”课程开始时准备的专业“








我想提请您注意反模式,我经常在Code Review StackExchange上的学生代码中遇到反模式,甚至在相当数量的其他教育材料中也遇到这种反模式。它们包含5个元素的数组;然后,由于幻数不好,它们引入了一个命名常数来表示基数“ 5”。



void example()
{
    constexpr int myArraySize = 5;
    int myArray[myArraySize] = {2, 7, 1, 8, 2};
    ...




但是解决方法一般!在上面的代码中,数字5重复:首先在值中myArraySize = 5,然后在实际分配元素时再次myArray从维护的角度来看,上面的代码同样糟糕:



constexpr int messageLength = 45;
const char message[messageLength] =
    "Invalid input. Please enter a valid number.\n";




-当然,我们没人会写。



重复的代码不好



请注意,在以上两个代码段中,每次更改数组的内容或消息的措辞时,都必须更新行代码,而不是一行。这是维护人员如何错误地更新此代码的示例



   constexpr int myArraySize = 5;
-   int myArray[myArraySize] = {2, 7, 1, 8, 2};
+   int myArray[myArraySize] = {3, 1, 4};




上面的补丁看起来像将数组的内容从2,7,1,8,2更改3,1,4,但事实并非如此!实际上,它将其更改为3,1,4,0​​,0(填充为零),因为维护者忘记了myArraySize根据进行调整myArray



可靠的方法



至于计数,计算机非常擅长于此。因此,让计算机计数!



int myArray[] = {2, 7, 1, 8, 2};
constexpr int myArraySize = std::size(myArray);




现在,您只需更改一行代码,即可将数组的内容从2,7,1,8,2更改3,1,4您无需在任何地方重复更改。



更重要的是,myArray实际代码通常使用for基于迭代器范围的循环和/或算法来对其进行操作,因此它根本不需要命名变量来存储数组的大小。



for (int elt : myArray) {
    use(elt);
}
std::sort(myArray.begin(), myArray.end());
std::ranges::sort(myArray);

// Warning: Unused variable 'myArraySize'




myArraySize始终使用 此代码的“不良”版本(在声明中myArray),因此程序员不太可能看到可以将其排除在外。在“良好”版本中,编译器很容易检测到myArraySize未使用的内容。



怎么做std::array呢?



有时,程序员朝着黑暗面又迈出了一步,并写道:



constexpr int myArraySize = 5;
std::array<int, myArraySize> myArray = {2, 7, 1, 8, 2};




至少应重写为:



std::array<int, 5> myArray = {2, 7, 1, 8, 2};
constexpr int myArraySize = myArray.size();  //  std::size(myArray)




但是,没有简单的方法可以摆脱第一行的手动计数。CTAD C ++ 17让您编写



std::array myArray = {2, 7, 1, 8, 2};




但这仅在需要数组时才有效int-short例如,在需要数组或array时则无效uint32_t



C ++ 20为我们提供了std :: to_array,这使我们可以编写



auto myArray = std::to_array<int>({2, 7, 1, 8, 2});
constexpr int myArraySize = myArray.size();




请注意,这将创建一个C数组,然后其元素移动(移动构造)到中std::array。我们前面的所有示例均myArray使用大括号的初始化程序列表进行初始化,该列表触发聚合初始化并在适当位置实例化数组元素。



无论如何,与良好的旧C数组(不需要模板实例化)相比,所有这些选项都会导致大量其他模板实例。因此,我强烈喜欢T[]较新的产品std::array<T, N>



在C ++ 11和C ++ 14中,std::array具有可以说的人体工程学优势arr.size();但是当C ++ 17为我们提供了std::size(arr)和内联数组。std::array没有更符合人体工程学的优势。如果需要其整个对象变量语义(将整个数组传递给函数!从函数返回数组!将数组分配为=!将数组与==!进行比较),请使用它,但否则建议避免使用std::array



同样,我建议避免使用std::list,除非您想要其迭代器的稳定性,快速粘贴,无需替换元素的排序等等,这并不是说C ++中没有这些类型的位置;我只是说他们有一套“非常特定的技能”,如果您不使用这些技能,您可能会付出高昂的代价。




结论:不要将马车围在马面前。实际上,甚至可能不需要推车。而且,如果您需要使用斑马来完成马匹的工作,那么您也不应将马车围在斑马前面。





阅读更多:






All Articles