C++中的一些困惑
文章目录
- C++中的一些困惑
-
- [1. using std::具体命名与using namespace std;](#1. using std::具体命名与using namespace std;)
- [2. 【int \*p[10] 】与 【int (\*p)[10]】](#2. 【int *p[10] 】与 【int (*p)[10]】)
- [3. main()函数可带参,参从何来?](#3. main()函数可带参,参从何来?)
- [4. constexpr函数的返回值可不为常量,那这时constexpr关键字作用是什么?](#4. constexpr函数的返回值可不为常量,那这时constexpr关键字作用是什么?)
- [5.为什么C++ STL不提供任何"树"容器?](#5.为什么C++ STL不提供任何“树”容器?)
- [6. 函数参数后面的`const`](#6. 函数参数后面的
const
)
这是个人笔记,记录一些学习过程中没有理解以及容易混淆的问题与解答
1. using std::具体命名与using namespace std;
在C++中,使用 using
和 using namespace
都是用来简化代码中命名空间的写法,但它们有不同的影响和使用场景,各有优劣:
-
using std::具体命名
- 优势:精确导入了命名空间中的特定名称,可以避免命名冲突,提高代码的可读性和可维护性。
- 劣势:需要逐个导入每个需要使用的名称,可能会显得冗长,尤其是当需要导入多个名称时。
cppCopy codeusing std::vector;
using std::cout;
using std::endl;int main() {
vector<int> nums = {1, 2, 3};
cout << "Hello, world!" << endl;
return 0;
} -
using namespace std;
- 优势:一次性导入了整个
std
命名空间中的所有名称,可以方便地使用标准库中的各种功能,节省了代码中重复写命名空间的时间和空间。 - 劣势:可能会引入命名冲突,尤其是当在代码中使用了其他命名空间或自定义了相同名称的变量或函数时,容易导致代码混乱和不可预测的行为。
cppCopy codeusing namespace std;
int main() {
vector<int> nums = {1, 2, 3};
cout << "Hello, world!" << endl;
return 0;
} - 优势:一次性导入了整个
总体来说:推荐使用 using std::具体的类型
的方式来导入特定的名称,因为它可以明确指定要导入的内容,避免了潜在的命名冲突问题,同时保持了代码的清晰性和可维护性。而在大型项目或者有多个命名空间交叉的情况下,最好避免使用 using namespace std;
这样的全局命名空间导入方式,以免引起不必要的麻烦。
2. 【int *p[10] 】与 【int (*p)[10]】
int *p[10]
- 这表示
p
是一个数组,包含了 10 个元素。 - 每个元素都是一个指向
int
类型的指针。 - 可以直接通过索引来访问数组中的指针,如
p[0]
表示数组的第一个指针。
- 这表示
cpp
int *p[10]; // p 是一个数组,包含了 10 个指向 int 的指针
int (*p)[10]
- 这表示
p
是一个指针,指向一个包含 10 个整数的数组。 p
指向的整个数组是一个单独的对象,而不是一个指向指针的数组。- 需要通过解引用来访问指针指向的数组元素,如
(*p)[0]
表示指针p
所指向数组的第一个元素。
- 这表示
cpp
int (*p)[10]; // p 是一个指针,指向一个包含 10 个整数的数组
举个例子来比较两者的使用:
cpp
int main() {
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *p1[10]; // 数组,包含 10 个指向 int 的指针
p1[0] = arr; // 将数组 arr 的首地址赋给 p1 的第一个指针
int (*p2)[10]; // 指针,指向包含 10 个整数的数组
p2 = &arr; // 将数组 arr 的地址赋给 p2
// 使用 p1 访问数组元素
for (int i = 0; i < 10; ++i) {
std::cout << *(p1[0] + i) << " "; // 输出数组元素
}
std::cout << std::endl;
// 使用 p2 访问数组元素
for (int i = 0; i < 10; ++i) {
std::cout << (*p2)[i] << " "; // 输出数组元素
}
return 0;
}
在上面的例子中,p1
是一个数组,包含了 10 个指向 int
的指针,而 p2
是一个指针,指向一个包含 10 个整数的数组。这两者的使用方式略有不同,因为 p1
中的每个元素都是指针,而 p2
指向的是整个数组对象。
3. main()函数可带参,参从何来?
在 C++ 中,main
函数可以具有带参数的形式,这些参数通常用于接收命令行参数。这些参数是由操作系统在程序启动时传递给程序的,并且可以在 main
函数的参数列表中接收到。
标准的 main
函数的声明有两种形式:
cpp
int main()
或者
cpp
int main(int argc, char* argv[])
第一种形式是不带参数的,程序启动时不会接收任何命令行参数。第二种形式是带参数的,其中 argc
表示命令行参数的数量,argv
是一个指向字符指针数组的指针,每个指针指向一个命令行参数的字符串。
argc
(argument count)表示命令行参数的数量,包括程序名称在内。argv
(argument vector)是一个指针数组,每个元素指向一个命令行参数的字符串,其中argv[0]
通常是程序的名称,argv[1]
、argv[2]
等依次是传递给程序的其他命令行参数。
例如,以下是一个简单的示例:
cpp
#include <iostream>
#include <string>
// 编写一个带实参的main函数并运行
int main(int argc, char* argv[]) {
// 确保有足够的实参
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " <arg1> <arg2>" << std::endl;
return 1; // 返回非零值表示错误退出
}
// 将实参连接成一个字符串
std::string result = std::string(argv[1]) + " " + std::string(argv[2]);
// 输出连接后的字符串
std::cout << "Concatenated string: " << result << std::endl;
return 0;
}
在VS code中编译运行:
-
打开Terminal,使用命令行编译源文件
g++ -o 生成的程序名(如:myprogram) 源文件.cpp
-
编译器会生成一个名为
myprogram
的可执行文件 -
在终端运行程序并传参
./myprogram hello world
执行过程及结果:
4. constexpr函数的返回值可不为常量,那这时constexpr关键字作用是什么?
虽然constexpr
函数的返回值并非总是常量,但使用constexpr
关键字可以告诉编译器,希望在可能的情况下在编译期间进行求值和优化 。这样可以使代码更加灵活并提高性能,特别是在一些需要进行编译期间计算的场景下,constexpr
函数非常有用。
进一步解释:
- 某些场景下,函数的返回值仍然可以再编译期间确定。(通常是因为函数的输入参数是常量表达式,或者函数内部只包含了可以在编译期间计算的操作),如:
cpp
#include <iostream>
// constexpr函数,参数是常量表达式
constexpr int add(int a, int b) {
return a + b;
}
int main() {
// 在编译期间就可以计算出结果
// 因为输入参数是常量表达式
constexpr int result = add(10, 20);
std::cout << "Result: " << result << std::endl;
return 0;
}
5.为什么C++ STL不提供任何"树"容器?
一个讨论:https://www.codenong.com/205945/
6. 函数参数后面的const
cpp
void func() const {};
是常量成员函数,即在函数内部不会修改对象的成员变量。
endl;
return 0;
}
## 5.为什么C++ STL不提供任何"树"容器?
一个讨论:https://www.codenong.com/205945/
## 6. 函数参数后面的`const`
```cpp
void func() const {};
是常量成员函数,即在函数内部不会修改对象的成员变量。