目录
1.命名空间
1.1namespace关键字
在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存 在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化, 以避免命名冲突或名字污染,namespace关键字的出现就是解决这种问题的。
#include <stdio.h>
#include <stdlib.h>
int main()
{
int rand = 10;
printf("%d",rand); //在C语言中,自定义rand与stdlib库内rand函数重名,无法正常使用
return 0;
}
1.2命名空间的使用
为了解决以上C的问题,我们可以自定义命名空间来避免与其他相同命名造成冲突。
#include <stdio.h>
#include <stdlib.h>
namespace hehe
{
int rand = 10;
}
using namespace hehe;//展开自定义hehe命名空间
int main()
{
printf("%d",rand);
printf("%d",hehe::rand);//若未展开命名空间,可添加域作用限定符
return 0;
}
C++提供了标准函数库的命名空间std,这样在使用标准库函数时,就不用依次添加std::这样的域作用限定符了。
#include <iostream>
using namespace std;
int main()
{
//std::cout << 1 << std::endl;//未展开std命名空间时
cout << 1 << endl;//展开std命名空间后
return 0;
}
在实际生产,std标准空间会给我们造成很大的不便,因此我们往往不展开std命名空间而仅仅展开其中一些较为常用的函数。
#include <iostream>
using std::cout;//只使用特定函数命名空间
using std::endl;
int main()
{
cout << 1 << endl;
cout << 2 << endl;
cout << 3 << endl;
return 0;
}
(尽管std标准命名空间在实际运用中有很大弊端,但在日常练习中,展开标准命名空间往往会方便很多)
2.缺省参数
2.1缺省参数的概念
缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实 参则采用该形参的缺省值,否则使用指定的实参。
void Func(int a = 0)
{
cout<<a<<endl;
}
int main()
{
Func(); // 没有传参时,使用参数的默认值
//结果为0
Func(10); // 传参时,使用指定的实参
//结果为10
return 0;
}
2.2缺省参数的种类
缺省参数分为全缺省和半缺省两大类。
全缺省:顾名思义,可以省去所有参数,因此在定义函数时要将所有变量设定初始化值。
//全缺省
void Func1(int a = 0)
{
cout << a << endl;
}
void Func2(int a = 10, int b = 20, int c = 30)
{
cout << "a " << a << endl;
cout << "b " << b << endl;
cout << "c " << c << endl;
}
半缺省:即可以省去部分参数,在定义时按照从右到左的顺序依次给出(不可间隔)
//半缺省
void Func(int a, int b = 10, int c = 20)
{
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
cout<<"c = "<<c<<endl;
}
注意:1.缺省函数不可在声明和定义中同时出现,为实用起见,最好放在声明处
2.缺省值必须是常量或全局变量
3.函数重载
3.1函数重载的概念
在实际开发中,有时候我们需要实现几个功能类似的函数,只是有些细节不同。例如希望交换两个变量的值,这两个变量有多种类型,可以是 int、float、char、bool 等,我们需要通过参数把变量的地址传入函数内部。在C语言中,程序员往往需要分别设计出三个不同名的函数,但是在C++中,函数允许拥有多个名字,只需形参列表(参数个数 或 类型 或 类型顺序)不同即可。
#include <iostream>
using namespace std;
//交换 int 变量的值
void Swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
//交换 float 变量的值
void Swap(float *a, float *b)
{
float temp = *a;
*a = *b;
*b = temp;
}
//交换 char 变量的值
void Swap(char *a, char *b)
{
char temp = *a;
*a = *b;
*b = temp;
}
//交换 bool 变量的值
void Swap(bool *a, bool *b)
{
char temp = *a;
*a = *b;
*b = temp;
}
int main()
{
//交换 int 变量的值
int n1 = 100, n2 = 200;
Swap(&n1, &n2);
cout<<n1<<", "<<n2<<endl;
//交换 float 变量的值
float f1 = 12.5, f2 = 56.93;
Swap(&f1, &f2);
cout<<f1<<", "<<f2<<endl;
//交换 char 变量的值
char c1 = 'A', c2 = 'B';
Swap(&c1, &c2);
cout<<c1<<", "<<c2<<endl;
//交换 bool 变量的值
bool b1 = false, b2 = true;
Swap(&b1, &b2);
cout<<b1<<", "<<b2<<endl;
return 0;
}
4.引用 ‘&’
4.1引用的概念
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空 间,它和它引用的变量共用同一块内存空间。
void Test()
{
int a = 10;
int& ra = a;//定义引用类型
//此时ra和a的值相同,地址也相同
}
注意:1. 引用在定义时必须初始化
2. 一个变量可以有多个引用
3. 引用一旦引用一个实体,再不能引用其他实体
int main()
{
int a = 0;
int& b = a;
int& c = b;
int& d = a;
cout << &a << &b << &c << &d << endl;//同一块空间
b++;
d++;
cout << a << endl;//a=2
int x = 11;
d = x;//x赋值给d,d仍是a的引用 引用不可修改地址
cout << a;//a=11
return 0;
}
4.2常引用
在引用的过程中,变量权限只可平移或缩小,不可被放大。
int main()
{
//❌引用过程中,权限不可被放大
//const int a = 0;
//int& b = a;
//✔ 没有放大权限 d为c的拷贝,d的改变不影响c
const int c = 0;
int d = c;
//✔ 引用过程中,权限可以平移或缩小
int x = 0;
int& y = x;
const int& z = x;
x++;//缩小z作为x的别名的权限,不改变x的权限
//z++; z作为别名权限被缩小
//整形提升/转换 时会产生临时变量,临时变量具有常属性(const),无法被修改
const int& m = 10;
double dd = 1.11;
int ii = dd;
//int& rii = dd; ❌ 类型转换时会产生int类型的临时变量 相当于权限放大
const int& rii = dd;//✔
//int& ret1 = func1();//❌ 权限放大 返回临时变量 具有常性
const int& ret1 = func1();//✔ 权限平移
int rret1 = func1(); //拷贝
int& ret2 = func2();//返回x的引用,权限平移
const int& rret2 = func2();//权限缩小
int i = 0;
double j = 1.11;
if (j > i)
{
cout << "xxxxx" << endl;
//运行结果为xxxxx
//运算符两侧不同时,会对两侧从小到大整形提升(用临时变量的形式)
}
return 0;
}
4.3引用的使用场景
4.3.1引用做参数
引用可以用做输出型参数,提高程序效率
void Swap(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
4.3.2引用做返回值
引用做返回值时,不生成临时变量,减少拷贝,提高效率
int& Count()
{
static int n = 0;//static修饰的变量存储于静态区
n++;
// ...
return n;
}
注意:大部分时候都可以用引用传参,但要谨慎使用引用做返回值,若对象出了作用域被销毁,则不可用引用返回
4.4引用和指针的区别
引用和指针的不同点:
1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
2. 引用在定义时必须初始化,指针没有要求
3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何 一个同类型实体
4. 没有NULL引用,但有NULL指针
5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32 位平台下占4个字节)
6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
7. 有多级指针,但是没有多级引用
8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
9. 引用比指针使用起来相对更安全
//引用和指针的区别
//没有NULL引用,有NULL指针
//引用更安全(没有空的概念,即不可使用未初始化的引用)
#include <iostream>
using namespace std;
int main()
{
int a = 10;
//语法上:不开辟空间,对a取别名
//底层汇编指令实现来看,引用和指针实现类似,也会开辟空间
int& ra = a;
ra = 20;
//语法上:开辟空间,存储a的地址
int* pa = &a;
*pa = 30;
return 0;
}
5.内联函数/auto/范围for
5.1内联函数
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调 用建立栈帧的开销,内联函数提升程序运行的效率。
//宏函数
//不需要建立栈帧 提高调用效率 复用性好
//复杂 易出错,代码可读性差,无法调试,缺少安全性检查
//#define Add(x,y) (x+y)*10
//内联函数
inline int Add(int x, int y)
{
return(x + y) * 10;
}
int main()
{
for (int i = 0; i < 10000; i++)
{
cout << Add(i, i + 1) << endl;
}
return 0;
}
内联函数使用时,声明和定义不可分离,即需要放在同一项目中。相较于宏函数,内联函数可以解决宏函数的全部问题;
内联函数适用于短小且调用频繁的函数,但是内联函数会导致可执行程序变大,代码膨胀;
inline对编译器只是建议,最总决定权归属编译器自身决定;
较长函数或递归函数函数即使加了inline也会被编译器否决。
5.2auto关键字
随着程序的不断发展,复杂化已经成为程序中不可避免的趋势,而这样就容易导致:1.拼写错误 2.类型错误。为了简便此类问题,auto关键字可以自动推导变量类型,减少低级错误。
C++11中,标准委员会赋予了auto全新的含义:auto不再是一个存储类型指示符,而是作为一 个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。
int main()
{
int a = 0;
int b = a;
auto c = a;//根据右侧表达式推到c的类型;
auto d = 1 + 1.11;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
vector<int>v;
vector<int>::iterator it1 = v.begin();
auto it2 = v.end();
std::map<std::string, std::string> dict;
std::map<std::string, std::string>::iterator dit1 = dict.begin();
auto dit2 = dict.end();//auto可以大幅简洁化代码
return 0;
}
注意:使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto 的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编 译期会将auto替换为变量实际的类型。同时,auto不可作为函数参数或用来声明数组。
5.3范围for
对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因 此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范 围内用于迭代的变量,第二部分则表示被迭代的范围。
void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
for(auto& e : array)
e *= 2;
for(auto e : array)
cout << e << " ";
return 0;
}
//与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环
注意:使用范围for时,for循环的迭代范围必须是确定的,对于数组而言即是数组首尾元素,对于类类型而言,应该提供begin和end来确定范围。
Fight!
本文介绍了C++中的命名空间,如何使用namespace避免命名冲突,以及缺省参数的概念和分类。接着讨论了函数重载,展示了如何通过不同的参数列表实现相同功能的多个函数。此外,还详细解释了引用的概念,包括常引用和引用的使用场景,以及引用与指针的区别。最后,提到了内联函数的作用和限制,auto关键字的自动类型推导功能,以及C++11引入的范围for循环的便利性。
368

被折叠的 条评论
为什么被折叠?



