[C++re_1] namespace | cout | 缺省参数 | 函数重载 | & vs 指针

发布于:2025-04-05 ⋅ 阅读:(20) ⋅ 点赞:(0)

目录

1.命名空间

展开命名空间域

2.输入输出

3.缺少参数

全缺省参数

半缺省参数

4.函数重载

参数类型不同

参数个数不同

参数类型的顺序不同

5.引用

按引用传递

返回引用

vs 指针


学校这半年开了 C++的课程,有一些实验报告要写,刚好借实验的机会,对部分语法进行一个复习总结叭,常复习常新~

详细代码可以前文查看:
1.C++入门(上)

2.C++入门(下)

1.命名空间

namespace A
{
	namespace B
	{
		namespace C
		{
			int a = 2;
		}
	}
}

int main()
{
	printf("%d", A::B::C::a);
	return 0;
}
  • 以上代码中,嵌套了三层命名空间域,在访问a时,从外层到内层按照A::B::C::a访问。
  • 所以可以按照如下方式解决不同文件变量可能存在冲突的问题:每个.cpp文件最外层,用一个命名空间域包含起来,后续引入文件时,每个人编写的文件独自享有一个域,就不会发生冲突问题了。

那如果这样的话,在main函数中想要访问其它文件内的内容是不是很冗余,几乎大部分变量都要加上::前缀,这就太麻烦了。于是又产生了展开命名空间域这一功能。

展开命名空间域
  • 所谓展开命名空间域,就是对某个空间域进行展开,将其内部的变量放到全局中。
using namespace (名称);

有时候我们并不是需要一个命名空间域中的所有内容,如果将整个空间域展开有些没必要。此时可以使部分展开

using (名称)::(变量名)
  • 示例代码
//展开全命名空间
namespace user1
{
	int a = 0;
	int b = 1;
}

using namespace user1;

int main()
{
	printf("%d", a);
	printf("%d", b);
	return 0;
}

//
//展开部分命名空间
namespace user1
{
	int a = 0;
	int b = 1;

	int Add(int x, int y)
	{
		return x + y;
	}
}

using user1::Add;
using user1::a;

int main()
{
	printf("%d",a);
	Add(3, 5);
	return 0;
}

匿名命名空间

  • C++中的命名空间可以匿名,此时这个命名空间无法从外部找到。
namespace 
{
    int localVar = 42;
}
  • 假如在一个文件的最外层,嵌套一层匿名命名空间,那么相当于给所有变量添加了static属性,文件外部无法访问文件内部的变量。

2.输入输出

  • 相比于C语言的输入输出,需要使用%d%s%f这样的占位符来控制输入类型。
  • C++的输入输出操作明显的优势就是:自动识别类型。
  • 其中cout可以拆分为c + out,所以用于输出;cin可以拆分为c + in,所以用于输入

3.缺少参数

全缺省参数
  • 缺省参数是值可以为函数的参数设置初始值,如果调用时没有传入参数,则此参数以初始值调用函数。
int Add(int x = 5, int y = 10)
{
	return x + y;
}

int main()
{
	Add(1, 2);  //1+2
	Add(1);		//1+10
	Add();		//5+10

	return 0;
}
  • 这种参数缺省叫做全缺省参数,即所有的参数都赋予了初始值,哪怕一个参数都不传,也可以调用函数。
  • 注意:传入参数必须从左往右传入,不能有空缺。
int Add(int x = 5, int y = 10, int z = 20)
{
	return x + y + z;
}

int main()
{
	Add(1, ,6);
	return 0;
}
  • 此代码中Add(1, ,6);的意图是让x = 5z = 20,让y取初始值。但是这是不允许的,调用函数时,必须从左向右连续传入,不能间断地缺省参数。

半缺省参数
  • 半缺省参数是指,缺省参数时,有一些值不赋予初始值,必须传入值。
int Add(int x = 5, int y, int z = 20)
{
	return x + y + z;
}

此代码中,x是被缺省的,那么其右边的yz也必须被缺省,不能跳过y直接缺省z

最后还有一个注意点:不能在声明和定义时同时缺省参数。

//.h

void func(int a = 10); // 函数声明

//
//.cpp

//重定义的错误写法
// void func(int a = 10) // 函数定义
// {
// 	cout << a * 5 << endl;
// }

void func(int a)
{
	cout << a * 5 << endl;
}
  • 将函数声明在了test.h文件中,又 定义在了test.cpp文件中。
  • 这样会造成重定义的错误,程序无法运行

如果想要将缺省参数声明在.h文件中,那么在定义时就不要写出缺省参数了。


4.函数重载

  • 函数重载是指C++允许在同一作用域中声明的同名函数
  • 但是其必须遵守一项规则:保证同名函数的形参列表不同。

形参列表不同要求满足以下三者之一:

  1. 函数的参数 个数不同
  2. 函数的参数 类型不同
  3. 函数的参数 类型的顺序不同
  • 原理就是编译器会根据识别到的形参,来选择调用哪个函数~

参数类型不同
void Add(int left, int right)
{
	cout << "I am int Add" << endl;
}

void Add(double left, double right)
{
	cout << "I am double Add" << endl;
}
参数个数不同
void f()
{
	cout << "f()" << endl;
}

void f(int a)
{
	cout << "f(int a)" << endl;
}

void f(int a, int b)
{
	cout << "f(int a, int b)" << endl;
}
参数类型的顺序不同
void f(int a, char b)
{
	cout << "f(int a,char b)" << endl;
}

void f(char b, int a)
{
	cout << "f(char b, int a)" << endl;
}

底层:去年文章中有详细解释过


5.引用

C++的引用是一种特殊的变量类型,用于给已经存在的变量起一个别名

  • 通过引用,可以通过一个已存在的变量名来访问和操作另一个变量的值。
  • 引用可以被看作是一个已存在变量的别名
  • 引用和被引用的变量始终指向同一块内存空间对引用的操作实际上就是对被引用变量的操作。
type& 别名 = 变量名;

其中,type是被引用变量的类型。

int main() {
   int num = 10;
   int& ref = num;    // 创建一个引用ref,指向num

   cout << "num的值为:" << num << endl;   // 输出:num的值为:10
   cout << "ref的值为:" << ref << endl;   // 输出:ref的值为:10

   // 通过引用修改num的值
    ref = 20;

    
   cout << "num的新值为:" << num << endl;   // 输出:num的新值为:20
   cout << "ref的新值为:" << ref << endl;   // 输出:ref的新值为:20

   return 0;
}

相当于:

int num = 10;
int* ref = &num;

*ref = 20;

需要注意的是,引用不同于指针,它不能指向空值或者没有初始化的变量。

因此,在定义引用时必须保证所引用的变量已经存在,并且在定义引用时必须进行初始化

也就是说下面的语句是非法的:

int& a;

///
//指针
int* a;
int* b = NULL;
  • a是一个引用,但是它没有初始化,此时编译器会报错。
  • 但是在指针中,空指针与未初始化的指针是合法的。

引用其实不单单只是代替指针这么简单,其还可以作为返回值,参数等。

按引用传递

C++中的按引用传递是一种参数传递方式,它允许函数通过引用来操作调用者提供的实参。

  • 按引用传递是将实参的引用传递给形参。
  • 按引用传递的语法:在函数的参数前加上&符号。
void Swap(int& a, int& b)  //采用引用,直接就可以 拿着用啦
{
	int tmp = a;
	a = b;
	b = tmp;
}

int main()
{
	int x = 1;
	int y = 3;

	Swap(x, y);

	return 0;
}


//
//对比C
void Swap(int* a, int* b)  //传指针
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

int main()
{
	int x = 1;
	int y = 3;

	Swap(&x, &y);  //取地址

	return 0;
}

按引用传递有以下几个作用:

1. 通过引用传递参数可以避免对大型对象的复制。

  • 当传递一个大型对象时,按值传递会进行一次复制操作
  • 而按引用传递只需要传递对象的引用而不需进行复制,从而提高了程序的效率。

2. 通过引用传递参数可以实现函数对实参的修改。

  • 在函数内部,通过引用可以直接操作实参,对实参的修改会在函数外部产生影响。
  • 而按值传递只能修改函数内部的形参副本,对实参没有影响。

返回引用

在C++中,返回引用是指从函数中返回一个引用类型的值。

  • 返回引用的主要目的是允许函数返回一个对于某个变量的引用,从而允许在函数外部对该变量进行修改。

返回引用的主要用途有以下几个:

  • 允许函数直接修改函数外部的变量。
  • 允许在函数调用中连续进行操作,类似于链式操作
  • 优化性能,避免创建临时对象。

下面通过案例来分别说明这几个功能:

1. 允许函数返回值,直接被函数外部修改

int& increment(int& num) 
{
  num++;
  return num;
}

int main()
{
  int num = 5;
  increment(num) = 10;
  cout << num << endl;  // 输出为 10
  return 0;
}

2. 允许函数返回值连续进行操作:

int& add(int& num, int value) 
{
  num += value;
  return num;
}

int main() 
{
  int num = 5;
  add(add(num, 3), 2);
  cout << num << endl;  // (5+3)+2 ==10
  return 0;
}
//第一次的返回值
//又被当作第二次的第一个参数进行了一个传入

3. 优化性能,避免创建临时对象:

string& concatenate(string& str1, const string& str2) 
{
  str1 += str2;
  return str1;
}

int main() 
{
  string str1 = "Hello";
  string str2 = " World";
  concatenate(str1, str2) += "!";
//通过返回引用,可以直接对str1进行修改,避免了创建临时对象
    
  cout << str1 << endl;  // 输出为 "Hello World!"
  return 0;
}

vs 指针

汇编对比

对比特性

引用 (Reference)

指针 (Pointer)

定义本质

变量的别名,与实体共享内存地址

储变量地址的独立变量

初始化要求

必须初始化,且绑定后不可更改绑定实体

允许不初始化(可能成为野指针)

指向可变性

始终指向初始化的实体,不可变更

可随时指向同类型的不同实体或置为 NULL

空值允许性

不存在空引用(NULL

引用非法)

允许指向 NULL

或空地址

sizeof结果

返回引用实体类型的大小

(如 int&返回 4

返回指针变量本身占用的地址空间大小

(如 8字节)

自增操作语义

对实体值自增(如 ref++

修改实际变量值)

地址偏移自增(如 ptr++

按类型大小移动地址)

多级间接访问

仅支持单级引用(如 int&

支持多级指针(如 int**

访问实体方式

隐式解引用(编译器自动处理)

显式使用 *

解引用(*ptr

安全性

更高(无空引用风险,绑定后不可变更)

更低(可能空指针、野指针、内存泄漏风险)

sum:

  1. 语义差异:引用是别名,指针是地址容器。
  2. 安全性:引用天然规避空引用和悬垂引用(需注意作用域问题),指针需手动管理有效性。
  3. 灵活性:指针支持更复杂的操作(如地址运算、多级间接访问),引用提供更简洁的语法。
  4. 使用场景:引用常用于函数参数传递和返回值优化,指针用于动态内存管理或底层操作。