C++入门_二
在C++编程中,命名空间、输入/输出和缺省参数是三个非常重要的概念。它们分别帮助我们组织代码、与程序进行交互以及为函数提供默认值。下面,我们将逐一探讨这些概念。
一. 命名空间(namespace)
在C++中,随着代码量的增加,变量、函数和类的数量也会不断增加。如果它们都位于全局作用域中,很容易发生命名冲突。为了避免这种情况,C++引入了命名空间的概念。
命名空间允许我们将相关的标识符组织在一起,并为它们提供一个唯一的名称。这样,即使不同的库或代码片段使用了相同的标识符名称,我们也可以通过指定命名空间来明确区分它们。
例如,我们可以定义一个名为bit
的命名空间,并在其中定义一些变量和函数:
namespace bit {
int num = 50;
void print() {
cout << num << endl;
}
}
要使用命名空间中的成员,我们可以使用作用域解析运算符::
例如:
cout << bit::num << endl;
bit::print(![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=data%3Aimage%2Fgif%3Bbase64%2CR0lGODlhAQABAPABAP%2F%2F%2FwAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw%3D%3D&pos_id=img-vuO4Q5cJ-1715345712673)
另外,我们还可以使用`using`关键字来简化对命名空间成员的访问。例如:
```cpp
using bit::num;
using bit::print;
int main() {
cout << num << endl;
print();
return 0;
}
使用using namesapace 可能会导致命名冲突和代码可读性下降,使用需谨慎!!!
二. C++的输入/输出操作
C++提供了cout
和cin
是最常用的输出和输入流对象。
要使用 cout he cin 需要使用头文件 ,并且引用 std 命名空或者使用 std::
#include <iostream>
using namespace std;
int main() {
cout << "Hello, World!" << endl;
int number;
cin >> number;
cout << number << endl;
return 0;
}
endl 是一个操纵符,它插入换行符。
三、缺省参数
缺省参数允许我们在定义函数时为参数提供默认值。这样,在调用函数时,如果省略了某些参数,它们将使用这些默认值。
缺省参数可以分为全缺省参数和半缺省参数。全缺省参数意味着所有参数都有默认值,而半缺省参数则意味着只有部分参数有默认值。
全缺省参数:
void Fun1(int num1 = 30, int num2 = 20, int num3 = 10)
{
cout << num1 << endl;
cout << num2 << endl;
cout << num3 << endl;
}
int main()
{
Fun1(1,2,3);
// 1,2,3
Fun2(1);
// 1,20,10
return 0;
}
我们可以选择提供所有参数、部分参数或不提供任何参数,函数将使用相应的默认值。
半缺省参数则要求从右到左依次给出默认值,中间不能间隔。例如:
void Fun2(int num1, int num2 = 20, int num3 = 30)
{
cout << num1 << endl;
cout << num2 << endl;
cout << num3 << endl;
}
int main()
{
Fun2(3);
return 0;
}
上面的函数num1 需要提供默认值。其他有默认值。
需要注意的是,缺省参数不能在函数声明和定义中同时出现。
内联函数
概念
在c++中有一些需要经常使用的函数add,swap这些
此时有了一个叫内联函数
对函数进行展开
inline int Add(int a, int b)
{
return a + b;
}
int main()
{
int c = Add(6 , 7);
cout << c << endl;
return 0;
}
就像这样使用
int c = Add(6 , 7);
mov edx,7
mov ecx,6
call Add (07FF6E76414A1h)
mov dword ptr [c],eax
但是我们看到仍然使用的是Add函数,因为调用了add函数的地址
为什么呢
因为在debug版本下不会展开的,目的方便调试
解决方法在在release模式下,查看编译器生成的汇编代码中是否存在call Add
特性
- inline是一种以空间换时间的做法,如果编译器按照内联函数处理,编译期间,会将函数体替换函数调用
有利亦有弊,缺点就是会让文件变大,优点效率更高。
- inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建
议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不
是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性
- inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到了
// F.h
#include <iostream>
using namespace std;
// 展开之后,没有函数地址W
inline void f(int i);
// F.cpp
#include "F.h"
void f(int i)
{
cout << i << endl;
}
// main.cpp
#include "F.h"
int main()
{
f(10);
return 0;
}
auto关键字
干什么的
在我们写入的程序越来越多,程序中的类型也越来越复杂,亦如:
1.类型难于拼写
2.含义不明确导致容易出错
#include <string>
#include <map>
int main()
{
std::map<std::string, std::string> m;
std::map<std::string, std::string>::iterator it = m.begin();
// 就像是这样的代码 可以使用 auto
auto it = m.begin();
return 0;
}
auto 可以自动推导变量类型
int a = 10;
auto b = 10;
auto c = 0;
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
// int int
// 上面函数输出变量类型
注意:使用auto定义变量必须对其进行初始化,在编译阶段编译器需要根据初始化表达式推到auto的实际类型
因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型
怎么用的
1.auto可以与指针结合起来使用
用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
int x = 10;
auto a = &x;
auto* b = &x;
auto& c = x;
2.在同一行定义多个变量
当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译
器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
auto a = 1,b = 2;
auto c = 3;d = 4.5; // 该行会报错,因为c和d的初始化表达式类型不同
auto 不能使用的场景
1.auto不能作为函数的参数
// auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
int swap (auto num)
{
// ...
}
2.auto不能直接用来声明数组
int a[] = {1,2,3};
auto b[] = {1,1,1};
基于范围的for循环(C++11)
在c++98中如果要遍历一个数组,可以按照一下方式进行
int array[] = {1,2,3,4,5};
for(int i=0;i<sizeof(array)/sizeof(array[0]);i++)
{
cout << array[i] << endl;
}
此时我们也可以使用我们之前的auto加上for组合,让循环更加简洁
int array[] = { 1,2,3,4,5 };
for (auto& e : array)
{
e *= 2;
}
for (auto e : array)
{
cout << e << endl;
}
使用auto条件
首先for循环的范围是确定的
指针空值nullptr(C++11)
在C语言中NULL实际是一个宏
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
可以看到NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。
这样让我们在使用时会遇到一些麻烦
void f(int)
{
cout<<"f(int)"<<endl;
}
void f(int*)
{
cout<<"f(int*)"<<endl;
}
int main()
{
f(0);
f(NULL);
f((int*)NULL);
return 0;
}
程序本意是想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,因此与程序的初衷相悖。
在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void *)0。
在C++中出现了一个另一个表示NULL的nullptr
1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。
2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
3.为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。
程序本意是想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,因此与程序的初衷相悖。
在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void *)0。
**在C++中出现了一个另一个表示NULL的nullptr**
**1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。**
**2. 在C++11中,sizeof(nullptr) 与 sizeof((void\*)0)所占的字节数相同。**
**3.为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。**