我想提请您注意反模式,我经常在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 ++中没有这些类型的位置;我只是说他们有一套“非常特定的技能”,如果您不使用这些技能,您可能会付出高昂的代价。
结论:不要将马车围在马面前。实际上,甚至可能不需要推车。而且,如果您需要使用斑马来完成马匹的工作,那么您也不应将马车围在斑马前面。
阅读更多: