> 文章列表 > 第1章、C++基础

第1章、C++基础

第1章、C++基础

第1章、C++基础

1.1 命名空间

1.1.1 命名空间的含义

在使用C++进行大规模程序设计时,开发过程往往是团队合作,各个程序员命名和各种C++库,对标识符的命名可能发生冲突,从而引进命名空间(一种特殊的域)的概念,以避免命名冲突或名字污染。
【例 1-1】命名冲突

// test1.cpp
#include <iostream>
using namespace std;
int a = 1;
int main()
{cout << a << endl;return 0;
}
// test2.cpp
int a = 2;

第1章、C++基础
第1章、C++基础
这个程序有test1.cpp和test2.cpp组成,两个文件中同时定义了全局变量 a ,引发命名冲突,故而 a 重定义;

1.1.2 命名空间的定义

C++中,命名空间使用namespace关键字声明,是一种特殊的域。

namespace 命名空间标识符名
{申明成员;// 可以是变量、函数、类、对象、结构体等
}

【例1-2】命名空间的定义

namespace Gredot1
{int a;namespace Gredot2{int b;}
}

从这个例子,我们可以看出,命名空间可以嵌套定义,即命名空间嵌套命名空间。在调用时,需要一步一步指定命名空间域。如果命名空间中找不到对应变量名,将会报错。

// 调用变量 b
Gredot1 :: Gredot2 :: b = 10;

这里" : : ",是作用域限定符,用来指定标识符的作用域。

1.1.3命名空间的使用

  • 直接指定标识符命名空间
Gredot1 :: a; 
  • 使用using namespace命令
using namespace Gredot1;
...
{a = 10;
}

这种方式是将命名空间完全展开,违背了命名空间设计的初衷,展开有风险,在大型项目中尽量避免使用这种方式。

  • 使用using 关键字
using Gredot :: a;
...
{a = 10;
}

注:同名命名空间

// test1.cpp
#include <iostream>
namespace Gredot1
{int a;
}
// test2.cpp
namespace Gredot1
{int b;
}

第1章、C++基础
同一个工程允许存在多个相同名称的命名空间,编译器最后合并成一个命名空间(集合思想)
第1章、C++基础
命名空间的展开和头文件展开的区别:

命名空间展开是指程序编译是是否会到命名空间搜索标识符
头文件展开会将头文件中的内容拷贝到程序代码中

1.2.C++的输入和输出

#include<iostream>
// std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中
using namespace std;
int main()
{cout<<"Hello world!!!"<<endl;return 0;
}

说明:

  • cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含< iostream >头文件中。
  • <<是流插入运算符,>>是流提取运算符。

为了兼容C语言C++的输入输出会对C语言的缓冲区进行同步(检查C语言输入缓冲区),所以cin/cout速度会慢于scanf/printf。

1.3 缺省参数

缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。

缺省值只能是常量或全局变量。

1、全缺省参数

void func(int a = 5, int b = 6, int c = 9)
{cout << a<< b << c << endl;
}
int main()
{func(1,2,3);//a接收1,b接收2,c接收3func(1,2);//a接收1,b接收2,c使用缺省值9fun();//a使用缺省值5,b使用缺省值6,c使用缺省值9return 0;
}

全缺省参数在调用时,不给参数时,将会使用缺省值调用。调用时,实参和形参从左到右匹配。

2、半缺省参数

void func(int a , int b = 6, int c = 9)
{cout << a << b << c << endl;
}
int main()
{func(1, 2, 3);//a接收1,b接收2,c接收3func(1);//a接收1,b使用缺省值6,c使用缺省值9func();//错误写法,因为形参a不是缺省,至少传一个参数return 0;
}

未使用缺省值的形参,在传参时必须传入,有缺省值的形参,根据需要可传可不传。

1、缺省必须从右往左缺省,不能a变量给了缺省值,b没给,c给了,这种跳跃的缺省语法是不被允许的。

2、缺省参数不能在函数声明和定义中同时出现,在声明中给了缺省值,定义中就不要给缺省值了!

1.4 函数重载

函数重载是函数的一种特殊情况,C++允许在同一作用域中声明多个同名函数,这些同名函数参数类型、参数个数、类型的顺序不同,常用来处理实现功能类似数据类型不同的问题。

1.4.1 什么情况可以重载

  • 函数参数个数不同 && 函数名相同
  • 函数参数类型不同 && 函数名相同
  • 函数参数顺序不同 && 函数名相同

1.4.2 函数重载原理

命名修饰规则(Linux演示)
第1章、C++基础第1章、C++基础在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中,所以就能根据形参,来区分同名函数。g++编译器函数名修饰规则【_Z+函数长度 +函数名+类型首字母】

问:为不能用函数返回值作为重载的标志?
答:因为就算函数名修饰规则中有函数返回类型的区分,我们在调用函数传参的时候,编译器是没办法知道我们想要调用哪个函数,会产生二义性。

1.5 引用

引用,就是已定义变量的一个别名。可以给变量取别名,也可以给变量的别名取别名,编译器不会为引用额外开辟空间,他们都代表着同一块空间。取的别名的类型必须与原变量保持一致。

1.5.1 引用特性

  • 引用在定义的时候必须初始化
int a = 10;
int&ra; // 未初始化的引用,错误写法
  • 一个变量可以有多个引用
int a = 10;
int&b = a;
int&c = b;
// b c都是a的别名
// 将相当于外号
  • C++ 引用不能改变指向
int a = 10;
int&b = a;
int x = 20;
b = x;  // 赋值操作,而非改变引用指向 

1.5.2 常引用(指针和引用的赋值,权限可以缩小不能扩大)

  • 对常量的引用
int main()
{const int& a = 10; // 用常引用作常量的别名return 0;
}

10是常量,有“常属性”,可读不可写,如果不加const修饰,那么a的权限就变成了可读可写,这是权限的放大,语法是不通过的,所以这里的a必须加上const修饰。(右往左)

  • 对const常量的引用
int main()
{const int a = 10;const int& ra = a;// 权限平移return 0;
}
  • 隐式、强制类型转换的引用
int main()
{double a = 10.256;const int& ra = a;// 权限平移  ra打印结果是10return 0;
}

变量a发生隐式类型转换,a将自身的数值截断后复制到临时变量中,ra对临时变量进行引用。临时变量具有常属性。所以这里要加const修饰。

1.5.3传参时的权限问题

  1. 权限只可以平移或缩小,不能放大。【右 - > 左】

2、缺省引用,必须使用const修饰
第1章、C++基础
3、const提升生命周期
值作为返回值时,由于临时变量出作用域销毁,此时会产生临时变量返回,引用作为函数返回值时,不会产生临时变量;
第1章、C++基础
fun函数的返回值使用临时变量带回,ret作为临时变量的引用,加const会延长临时变量的生命周期;
3、引用的使用场景

1.5.4 引用做参数

void Swap(int& a, int& b)
{int tmp=a;a = b;b = a;
}

引用作为函数的形参,函数将直接操作a,b变量本身,而不会产生拷贝

引用做参数的优势

  • 减少拷贝,提高效率

  • 作为输出型参数,在函数中,形参改变会改变实参。

  • 一般引用做参数的(函数中形参不需要改变)都要用const引用

1.5.5引用做返回值

错误用法:

int& Add(int a, int b)
{int sum = a + b;return sum;  // sum 为局部变量
}
int main()
{int ret = Add(1, 2);cout << ret << endl;return 0;
}

当Add函数调用完毕后,函数栈帧被销毁,同时通过临时变量将sum的别名返回,但是sum它已经被销毁了。所以这是一种错误的用法。

正确用法:

int& Add(int a, int b)
{static int sum = a + b;return sum;   // sum 为全局变量
}
int main()
{int ret = Add(1, 2);cout << ret << endl;return 0;
}

总结:函数栈帧结束后,如果返回值不会随着栈帧的结束而销毁,那么就可以使用引用返回。

引用返回相较于传值返回的优势
1、减少拷贝,提高运行效率

2、可以直接对返回值进行修改

1.5.6 引用和指针的区别

1、语法上引用是变量的别名,不开空间,而指针存储一个变量地址。但是引用的底层是用指针实现的。

2、引用在定义时必须初始化,指针没有要求

3、引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体 。这就导致了C++的引用代替了一部分指针的功能,但不能完全代替。例如链表中的next必须用指针实现。如果采用引用的方式引用节点,插入删除节点要修改next,没办法通过引用改变next指向的值。

4、没有NULL引用,但有NULL指针

5、在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32

位平台下占4个字节)

6、引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小

7、有多级指针,但是没有多级引用

8、访问实体方式不同,指针需要显式解引用,引用编译器自己处理

9、引用比指针使用起来相对更安全