命名空间namespace
- namespace :命名空间,相当于定义了一个作用域,用来解决命名冲突问题。
- 域作用限定符:访问命名空间中的内容,使用双冒号 : : ,当: :左边为空时,表示使用全局域。
// 定义第一个命名空间
namespace first_space{
void func(){
int a = 1;
cout << "Inside first_space" << endl;
}
}
// 定义第二个命名空间
namespace second_space{
void func(){
int a = 2;
cout << "Inside second_space" << endl;
}
}
int a = 10;
int main ()
{
int a = 5;
printf("%d\n",a);
//a = 5,就近原则,使用a = 5的那个。
printf("%d\n",first_space::a);
//a = 1
printf("%d\n",::a);
//a = 10,::左边为空,代表使用全局域(全局变量)
// 调用第一个命名空间中的函数
first_space::func();
// 调用第二个命名空间中的函数
second_space::func();
return 0;
}
- using :using namespace 指令,这个指令会告诉编译器,后续的代码将使用指定的命名空间中的名称,这样在使用命名空间时就可以不用在前面加上命名空间的名称。
using namespace first_space;
int main ()
{
// 调用第一个命名空间中的函数
func();
return 0;
}
- 嵌套命名空间,可以使用: :来访问嵌套的命名空间。
namespace namespace_name1 {
// 代码声明
namespace namespace_name2 {
// 代码声明
}
}// 访问 namespace_name2 中的成员
using namespace namespace_name1::namespace_name2;
// 访问 namespace_name1 中的成员
using namespace namespace_name1;
- C++库为了防止命名冲突,将自己库中的东西都定义在一个 std 的命名空间中
- 所以有时候程序中都会调用 using namespace std; 这个库。
#include //引用.h头文件,为了与老版区分,在新版中不用写.h
using namespace std;
//调用std库int main()
{
cout << "hello world" << endl;
//因为上面写了using,所以可以不写命名空间
std::cout << "hello world" << std::endl;
//指定命名空间
return 0;
}
C++输入和输出
- 【C++入门】C++的输入输出可以自动识别类型,不用手动表明。
#include
using namespace std; int main() { int a = 10; cout << a << endl << "hellow world" << endl; //cout表示输出,endl表示换行 cin >> a ; //cin表示输入相当于scanf,但是不用指定类型,会自动识别。 return 0; }
- 在定义函数时,可以给参数赋默认值(这个默认值就叫缺省值),当调用函数没有传递参数时,该参数就会使用默认值
- 注意:给参数赋缺省值时,只能从右到左,没有缺省值的必须都在左边。
- 可以全部缺省,也可以半缺省。
void test(int a, int b = 0, int c = 10)//半缺省
{
cout << a << endl;
cout << b << endl;
cout << c << endl;
}int main()
{
test(1);
//输出为 1010
test(1,2,3);
//输出为 1 2 3
return 0;
}
函数重载
- C语言中不允许定义相同名称的函数,但是C++中可以
- 但是C++中定义相同名称的函数时,函数的参数必须不同(参数类型或个数不同,与有无返回值和缺省参数无关)
- 定义相同名称的函数,就叫函数重载。
int add(int a,int b)
{
}int add(char a,int b)
{
}char add(int a,int b,int c)
{
}int add(int a = 1,int b = 2,int c = 3) //注意:这个函数和上一个函数就不是重载
{
}
引用变量
- 引用不是新定义一个变量,而是给已经存在的变量取了个别名,实际内存中使用的是同一块空间。
- 引用必须在定义时初始化,引用一旦初始化后,引用对象不会变(初始化时不能为nullptr)
int a =10;
int d = 6;
int& b = a;
//b是a的别名(引用)
b = d;
//这里b不是d的别名,这里是将d的值赋值给b。
int& c;
//未初始化,会报错。
- 注意:引用变量的权限不能比原变量大
const int a = 10;
int& c = a;
//原变量a是const常量,不可更改,而引用c是int类型,可以更改
//权限放大,这是错误的int a = 10;
const int& c = a;
//a可以更改,c不可更改,权限缩小,这是对的。
- 在发生隐式类型转换时,会创建一个临时变量,这个临时变量是const类型的。
int a = 10;
double& ra = a;
//这是错误的
const double& ra = a;
//这是对的
//因为隐式类型转换,会创建一个const double的临时变量
//ra引用的就是这个临时变量,是临时变量的别名
引用变量作函数返回值
- 传值返回:返回的是 c 的拷贝
- return c 时返回的并不是c这个变量,而是创建一个临时变量(const int 型),将c的值传给临时变量,再由临时变量传递给 ret (c是局部变量)
- 所以如果要用引用来接收返回值,需要加const (临时变量是const类型)
int add(int a,int b)
{
int c = a + b;
return c;
}
int main()
{
int ret = add(1,2);
const int& ret = add(1,2);
return 0;
}
- 传引用返回:返回的是 c 的引用
- return c 时返回时创建的临时变量是 int& 类型
- 也就是说ret接收到的是变量 c 的引用
- 注意:变量c是局部变量,出函数后,就销毁,所以ret 的值是不确定的,ret代表的内存空间还是之前变量 c 的地址。
- 当参数和返回值是占用的内存比较大的时候,传引用传参和引用返回可以大大提高效率。
int& add(int a,int b)
{
int c = a + b;
return c;
}
int main()
{
int& ret = add(1,2);
//这里创建的临时变量是int&类型,所以可以不加const。
//这里 ret 的值是随机的。add(5,7);
cout << ret << endl;
//这里输出结果为12
//因为int& ret = add(1,2);
调用后,栈中内存被销毁,但是ret代表了之前变量c的地址
//add(5,7);
再次调用,会在原来的栈内存中去生成这个函数,新生成的函数中变量c的地址还是原来的地址。
//注意:必须是两次连续调用才会出现这种情况,因为上一次销毁内存后,没有其他数据占用栈空间
//其实这里是越界访问了的。
return 0;
}
引用与指针的关系
- 从语法的角度:
- 引用并没有开辟新空间,只是给原来的空间取了个别名
- 而指针是开辟了一个新空间,用来存储原变量的地址。
- 从底层汇编的角度:
- 引用跟指针是相似的,引用的底层的处理方式跟指针一样,都是开辟新的指针空间。
- 用 inline 修饰的函数叫内联函数,编译时C++编译器会在调用内联函数的地方展开,不存在函数压栈的开销,提升程序的效率。
- 直接调用函数,会在栈中创建这个函数,有栈帧消耗(每次调用函数都会在栈中创建)
- C++中建议频繁调用的小函数定义成内联函数,避免栈帧开销。
- 使用内联函数的缺陷:编译出来的可执行程序变大,执行程序消耗内存变多。
- 内联函数是一种以空间换时间的做法,所以不适合代码很长或者有递归/循环的函数。
- 注意:
- 内联函数声明和定义必须放在同一文件或者在定义函数时直接声明,否则会在编译时找不到目标。
- 内联函数不能用于缺省参数的函数中。
//inline void test(int a, int b , int c );
inline void test(int a, int b , int c )//定义函数时直接声明
{
cout << a << endl;
cout << b << endl;
cout << c << endl;
}
int main()
{
test(1, 2, 3);
return 0;
}
关键字auto
- auto:自动匹配类型
- auto会根据a的类型自动定义b的类型。
int a = 10;
auto b = a;
- typeid().name() 打印变量类型
cout << typeid(b).name << endl;
范围for遍历(C++11新语法)
- 功能:自动遍历,自动将arr中的值赋给 a,直到结束。
- 注意:arr 必须有确定的范围。
int arr[] = {1,2,3,4,5,6}
for(auto a : arr) //这里相当于 int a = arr[0],然后依次循环
{
a *= 2;
//这里a 是一个独立的变量,与arr[0]无关。
}for(auto& a : arr) //这里相当于 int& a = arr[0],然后循环
{
a *= 2;
//这里的 a 是arr[0]的引用(别名),所以改变 a 也会改变arr[0]的值
}
推荐阅读
- 数据结构与算法(c++)|【20. 滑动窗口】
- 数据结构与算法(c++)|【19. 单调栈】
- C|【c ++ primer 笔记】第4章 表达式
- C|【c ++ primer 笔记】第3章 字符串、向量和数组
- 数据结构与算法(c++)|【18. 模拟栈和队列】
- cpp|cpp 模板函数,类模板(1)
- 初学至学会C++|初阶C++——C++第二节——类和对象(大全篇)
- 初学至学会C++|初阶后的C++——第六节——IO流 与 继承
- C++|【C++】探讨迭代器的秘密 和 迭代器失效问题(学精、学好必知)