c++入门问题

C++ Primer Questions

本文关键字:问题 c++      更新时间:2023-10-16

我目前正在阅读Lahoie, Lippman和Moo编写的第5版c++ Primer,并一直在努力解决一些问题。

首先,我只是想确认,当使用任何cctype函数时,我必须确保我包括标题,对吗?因为一开始,我忘了包含它,但它仍然运行。这让我很困惑。

另外,我正在浏览一个不同的问题(我将得到),并发现了另一个问题哈哈!当使用来自cctype的任何东西时,我应该把它写为std::/write using std::,例如,如果我使用tolower,要么在每个实例上写std::tolower/为它写一个using语句。这是有意义的,因为它确实说它们是"在std命名空间中定义的",但我没有意识到,并且一直在写它,没有问题。我猜size_t也差不多,对吧?

说到size_t,我有一个问题。这是我的代码:
// Exercise Section 3.5.2., Exercise 3.30
#include <iostream>
#include <cstddef>
using std::cout; using std::endl;
int main()
{
    constexpr size_t num_size = 10;
    int num[num_size] = {};
    for (size_t n = 0; n < num_size; ++n) {
        num[n] = n;
        cout << num[n] << endl;
    }
    return 0;
}

因此,代码应该定义一个包含10个int的数组,并为每个元素赋予与其在数组中的位置相同的值。

它运行正确,但我在num[n]=n部分收到错误。上面写着Implicit conversion loses integer precision: size_t (aka 'unsigned long') to int

我明白这意味着什么,但我的问题是,这本书说"当我们使用一个变量下标数组,我们通常应该定义该变量类型size_t"。我做过这个,它给出了这个错误。它确实运行得很好,但似乎这种事情可能会导致错误。

注:在这段代码中,就像我上面问的,我应该有using std::size_t吗?

我必须包括头,即使它没有工作吗?

是的,你必须总是包含至少一个头文件来提供你需要的每一个定义/声明,除非确切的原型/类型定义是有保证的,并且你直接把它放到你的源代码中。

一些标准头文件可能包含其他标准头文件,这可能会让你在某些时候侥幸逃脱,但当你升级/移植到不同的实现时,你会后悔的。

我读到所有的声明和定义都是"在std命名空间中定义的",但我没有意识到,并且一直在写它,没有问题。我猜size_t也是类似的,对吧?

是的,是一样的。出于兼容性的考虑,C/Unicode所采用的<c...>头也可以在全局命名空间中提供它们的符号。

17.6.1.2标头[headers]

1 c++标准库的每个元素(视情况而定)在头文件中声明或定义。175
c++标准库提供了55个c++库头,如表14所示。
C标准库的功能在26个附加头中提供,如表15所示。
除第18条至第30条和附件D中指出的情况外,每个头文件cname的内容应与相应的头文件名称。h的内容相同,如C标准库(1.2)或C Unicode TR(视情况而定)中规定的,就像通过包含一样。然而,在c++标准库中,这些声明(在C中定义为宏的名字除外)在命名空间std的命名空间范围(3.3.6)内。这些名字是否首先在全局命名空间范围内声明,然后通过显式using-declarations(7.3.3)注入命名空间std,这是未指定的。
在C中被定义为宏的名字在c++标准库中也应该被定义为宏,即使C授予作为函数实现的许可。[注:C语言中定义为宏的名称包括:assert、offsetof、setjmp、va_arg、va_end和va_start。]-end note]
在C中定义为函数的名称,在c++标准库中也应定义为函数。176
在c++中作为关键字或操作符的标识符不能在c++标准库头文件中定义为宏。177
C标准库头文件描述了在c++程序中使用name.h (C头文件)格式的效果。178

在我的示例程序中,我使用size_t作为数组索引。这招管用,不过我得到了警告。我应该这样做吗?它通常会导致错误吗?

当然,正如你所猜测的,名称空间std也同样适用。

至于其他的,有一个很好的短语:"好的建议是有理由的"。

你应该使用std::size_t作为索引的原因是,这个类型a)向读者表明它是一个大小或索引,b)保证它足够大。在您的例子中,一个较低的int,保证其最小最大值为215-1,就可以了。
另一种方法是在赋值时直接转换为正确的类型。

你问:

首先,我只是想确认,当使用任何cctype函数时,我必须确保我包括标题,对吗?

是的,是这样的。您可能会间接获得一些函数声明或其他声明,但这不是可移植代码。您应该理解标准中声明在哪里可用,并在使用函数、类型等之前包含该头文件。

你问:

我猜size_t也类似,对吧?

是的,你应该使用std::size_t

有一个关于这个话题的帖子。浏览size_t和std::size_t的区别

你问:

在这段代码中,就像我上面问的,我应该使用std::size_t吗?

在循环中,也可以使用int。使用std::size_t索引数组的建议是一个很好的建议,但并不是不可违背的。

如果您选择使用size_tn,它是可以使用static_cast转换为int,以摆脱编译器的警告/错误。

num[n] = static_cast<int>(n);

你声明你的数组为

int num[num_size] = {};

这意味着它是一个包含10元素的int数组。

然后你说

for (size_t n = 0; n < num_size; ++n)
    num[n] = n;

注意n的类型是size_t,也就是unsigned long。因此,您将unsigned long值放入int数组中,因此它们被隐式转换为int值。

标准头文件在全局命名空间中放置了一堆东西。理想情况下,他们不会,但他们确实会。通常这是因为有些东西实际上是一个宏,而不是一个类型定义函数。

有时可以不包含头文件,因为您包含的其他头文件包含了缺失的头文件。

我必须确保包含标题,对吧?因为,一开始,我忘了包含它,但它仍然运行。

一些标准标头可以包含其他标头。但是,如果程序中使用了头文件中的一些声明,则显式地包含头文件是一个好主意。在包含的头文件的其他实现中,可能会出现这样的情况:所需的头文件不包含在内,编译器将发出错误。

。这将是有意义的,因为它确实说它们是"定义在std命名空间",但我没有意识到,并且一直在写它还没出过问题。

c++标准允许编译器将C标准函数放在全局命名空间中。尽管在这种情况下,最好显式地指定在任何情况下都要声明函数的命名空间std。

对于最后一个问题,那么数组的元素具有类型int,而你分配给它们类型size_t的值,问题是类型int不能容纳类型size_t的所有值,编译器会警告你这一点。你可以显式地指定强制转换,告诉编译器你知道你在做什么。

num[n] = ( int )n;

num[n] = static_cast<int>( n );