命名空间
在C++中,命名空间(Namespace)是一种用于组织代码的机制,以防止名称冲突。命名空间允许组织代码中的符号(如类名、变量名、函数名等),使得同一符号在不同命名空间中可以代表不同的实体。这对于管理大型项目中的名称非常有用,特别是在涉及多个库时。
命名空间和作用域:
- 命名空间(Namespace): 在编程中,特别是在像C++这样的语言中,命名空间是用于区分不同代码段中相同名称的一种方法。它们帮助避免命名冲突,并且可以组织代码,使其更加模块化。
- 作用域(Scope): 在编程中,作用域是一个变量或函数可以被访问的区域。在一个作用域内声明的变量只能在该作用域内被访问。作用域可以是局部的(如在函数内部)或全局的。
定义命名空间的几种方式
定义命名空间
namespace 名称 {
// 代码,例如类、函数、变量等定义
}
命名空间的几种主要使用方式
加命名空间及作用限定符(指定命名空间访问)
int main()
{
printf("%d\n", N::a);
return 0;
}
使用using部分展开
using N::b
int main()
{
printf("%d\n", N::a);
printf("%d\n", b);
return 0;
}
全局展开
using namespce N;
int main()
{
printf("%d\n", N::a);
printf("%d\n", b);
Add(10, 20);
return 0;
}
命名空间的几个特质
命名空间的跨文件
输入&输出
在C++中,cin
和 cout
是iostream库中定义的两个对象,分别用于标准输入和标准输出。cin
通常与提取运算符 >>
结合使用来从标准输入(如键盘)读取数据,而 cout
通常与插入运算符 <<
结合使用来向标准输出(如屏幕)写入数据。
#include <iostream>
int main() {
int number;
std::cout << "Please enter a number: "; // 提示用户输入一个数字
std::cin >> number; // 从标准输入读取一个整数
std::cout << "You entered: " << number; // 输出用户输入的数字
return 0;
}
<<
运算符用于将数据“插入”到输出流中,而 >>
运算符用于从输入流中“提取”数据。
你可以将多个 <<
或者 >>
运算符链接起来,以便在单个语句中进行多次插入或提取操作。
例如,从用户连续读取两个数字:
int a, b;
std::cin >> a >> b;
或者连续打印多个值:
std::cout << "First number: " << a << ", Second number: " << b;
缺省参数
在编程中,缺省参数(又称默认参数)是函数或方法参数列表中的一种特性,允许在定义函数时为一个或多个参数指定一个默认值。如果在调用函数时没有为这些参数提供值,就会使用这些默认值。这允许在不影响现有代码调用的情况下增加新的参数,也使得函数调用更加灵活。
using namespace i;
void Func(int a = 0)
{
cout << a << endl;
}
int main()
{
Func();//这里的的a并没有输入变量,但是它还是会直接等于0
Func(10);
return 0;
}
全缺省
其中所有的参数都有默认值
void Func(int a = 10, int b = 20, int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
}
int main()
{
Func();
Func(1);
Func(1, 2);
Func(1, 2,3);
return 0;
}
半缺省
其中一些参数有默认值,而其他一些则没有。这意味着对于有默认值的参数,调用函数时可以不提供实际的参数值,这些参数会自动使用定义函数时指定的默认值。
//半缺省,(缺省值只能从右往左给,必须连续给
//不能定义和声明都给一个缺省参数
//也不能只给定义
//只能声明给
void Func(int a , int b = 20, int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
}
int main()
{
Func(1);
Func(1, 2);
Func(1, 2, 3);
return 0;
}
注意事项
1. 半缺省参数必须从右往左依次来给,不能隔着给
2. 缺省参数不能再函数声明和定义中同时出现
3.缺省值必须是常量或者是全局变量
重载
函数重载是一种编程特性,它允许同一个程序中存在多个同名函数,但这些函数的参数列表必须不同。参数列表的不同可以是参数的数量不同,参数的类型不同,或者参数的顺序不同(如果参数类型不全相同)
//1、参数类型不同 (类型不同,个数不同)返回值可同也可以不同
int Add(int left, int right)
{
cout << "int Add(int left, int right)" << endl;
return left + right;
}
double Add(double left, double right)
{
cout << "double Add(double left, double right)" << endl;
return left + right;
}
函数重载的规则:
- 重载的函数必须在参数类型、数量或顺序上有所不同。
- 不能仅通过返回类型的不同来重载函数。
- 当调用一个重载的函数时,编译器会根据传递的参数类型来确定使用哪个函数。
名字修饰规则
函数的签名包括函数名和参数列表(包括参数的类型、数量、顺序)。每个重载函数都必须有一个独一无二的签名。请注意,函数的返回类型不是函数签名的一部分,因此不能仅通过返回类型来区分重载的函数。
Linux的名字修饰规则:函数名加上参数类型的前缀
windows的名字修饰规则
引用
在C++中,引用是一种复合类型,它是某个已存在变量的别名。引用的工作方式类似于指针,但有几个关键区别:引用一旦被初始化为某个变量的引用后,就不能再指向另一个变量,而指针可以在任何时候指向另一个地址。此外,引用本身不是一个可以操作的独立对象,它没有自己的内存地址,而指针是一个实际的对象,有自己的内存地址。
引用在声明时必须被初始化,并且不能为null
。引用通常用于函数参数传递,使得函数可以直接操作实参变量,而不仅仅是操作其副本。这样不仅可以提高效率(特别是对于大型对象的操作),还可以允许函数修改其参数的值。
引用的使用场合:
-
函数传参:使用引用作为函数参数可以避免拷贝大型对象,同时也可以让函数修改传入对象的值。
-
函数返回值:函数可以返回一个对象的引用,这样可以避免返回值时的对象拷贝。但要小心不要返回局部对象的引用,因为局部对象在函数结束时会被销毁。
-
操作符重载:在重载赋值操作符
=
、下标操作符[]
、解引用操作符*
和箭头操作符->
等时,经常需要返回引用类型。
函数传参
引用就是这个变量的别名,所以当这个数被传进了swap这个函数中的时候,它拥有了一个新名字,left。 而传地址,是在函数中创建一个指针,指向的地址是变量的地址,这个时候解引用指针就可以找到这个地址进行更改了
区别就是引用不占用空间,和原变量使用一块新的地址。而指针在创建的时候,会请求一块空间存放指针
函数返回值
在左侧的流程图中:
- 主要描述了
main
函数调用Count
函数的流程。 Count
函数内部有一个局部静态变量n
,它的值在函数调用之间是持久的。Count
函数返回n
的引用给main
函数。- 流程图指出这种返回引用的方式是不安全的,因为它可能导致返回的引用指向一个不再有效的局部静态变量(尽管在这种情况下,由于
n
是静态的,它在程序的整个运行期间都是有效的)。
操作符重载
#define N 10
struct Array
{
int& at(int i)
{
assert(i < N);
return a[i];
}
int& operator[](int i)
{
assert(i < N);
return a[i];
}
int a[N];
int size;
}AY;
//临时变量具有常性不能修改
//1. 减少拷贝
//2. 调用者可以修改返回对象
int main()
{
Array ay;
for (int i = 0; i < N; ++i)
{
//ay.at(i) = 0;
//ay.operator[](i) = 0;
ay[i] = 0;
}
for (int i = 0; i < N; ++i)
{
cout << ay[i] << " ";
}
cout << endl;
return 0;
}
引用做返回值才能使用这些,因为这些直接做成返回值,属于是一个临时变量,但是临时变量具有常性,不能改变 ,所以不能作为左边的赋值,但是如果是引用的话,就可以代表这个数的别名,可以进行操作
关于类型转换生成临时变量
在C++中,当发生隐式类型转换(也称为类型提升)时,如果是从一个非const类型转换到另一个类型并试图将结果绑定到一个const引用上,编译器会创建一个临时变量来存放转换后的值。这个临时变量是const的,意味着你不能通过引用来修改它的值。
这样做的原因是确保类型安全和对象的不变性。当你将一个临时变量的引用绑定到一个const引用上时,编译器保证你不能修改这个临时变量,因为临时变量的生命周期很短,只在表达式的持续期间存在。如果允许修改,那么修改可能会在没有任何对应变量存在的情况下发生,这会导致不可预测的行为。
//常引用
int main()
{
//权限不能放大
const int a = 10;
int b = a;
//int& b = a;
const int& b = a;
//权限可以缩小
int c = 20;
const int& d = c;
const int& e = 10;
//int& e = 10;//不可以,权限放大了
int i = 1;
double j = i;
const double& rj = i;
//指针的权限也不能放大,可以缩小
const int* p1 = NULL;
//int* p2 = p1;
//权限保持
const int* p1 = NULL;
const int* p2 = p1;
return 0;
}