命名空间
命名冲突(为什么使用命名空间)
情景一:
有时我们定义了一个全局变量/函数/结构体类型,
然而库里也恰好定义了相同名称的全局变量/函数/结构体类型,
就会出现 重定义 或 分不清是变量、函数还是结构体类型 的问题.
#include <string.h>
#include <stdio.h>
int strcpy = 0;
int main()
{
printf("%d",strcpy);
return 0;
}
情景二:
一个项目在多组协作的情况下,也会出现名字冲突的问题.
4个小组都定义了相同名称的结构体类型,而且内部成员可能还不同.
于是c++便引入了命名空间.
命名空间的使用
定义命名空间
namespace yh //yh是命名空间的名称,可以自己取名
{
//里面可以定义变量、定义函数、定义类型
int variable;
void function()
{
printf("hello world\n");
}
struct ListNode
{
int data;
struct ListNode* next;
};
}
访问命名空间的内容
1 指定命名空间访问.
:: 域作用限定符 —— 表示要去符号左边的域中找符号右边的内容
以上面定义的命名空间为例
int main()
{
//去yh这个命名空间域中找到variable这个全局变量
printf("%d\n", yh::variable);
//去yh这个命名空间域中找到function()这个函数
yh::function();
//特殊:命名空间域要放到struct后面
struct yh::ListNode node;
node.data = 0;
node.next = NULL;
printf("%d %p", node.data, node.next);
return 0;
}
2 直接将整个命名空间展开
using namespace yh
此时不需要指定命名空间域去找对应的成员.
相当于不仅会去全局域中找,还会自动到已展开的命名空间域中找.
然而,若展开命名空间后会有命名冲突的风险.
展开命名空间域不代表这个命名空间域就成了全局域,仍然可以通过指定域访问避免命名冲突
那还不如不展开.....
3 展开某个命名空间中常用的变量/函数/类型
using yh::function;
using yh::ListNode; //这里就不能有struct
int main()
{
//去yh这个域中找到variable这个全局变量
printf("%d\n", yh::variable);
function();
struct ListNode node;
node.data = 0;
node.next = NULL;
printf("%d %p", node.data, node.next);
return 0;
}
特殊
1 命名空间可以嵌套.
namespace yh
{
int variable;
namespace ll
{
void print()
{
printf("hello world\n");
}
}
}
int main()
{
//访问variable
yh::variable;
//访问print()
yh::ll::print();
return 0;
}
2 在同一工程中(不同文件)允许存在多个相同名称的命名空间
编译器最后会把它们合到同一个命名空间中.
同一工程中:
如果在多个文件中或同一文件定义了多个相同名称的命名空间,编译器会把它们合在一起.
#include "test.h"
#include "test2.h"
//test.cpp
namespace yh
{
namespace ll
{
void print()
{
printf("hello world\n");
}
}
}
int main()
{
//访问print()
yh::ll::print();
//访问function1()
yh::function1();
//访问function2()
yh::function2();
return 0;
}
//test.h
#pragma once
#include <stdio.h>
namespace yh
{
void function1()
{
printf("hello world1\n");
}
}
//test2.cpp
#include "test2.h"
void yh::function2()
{
printf("hello world2\n");
}
//test2.h
#pragma once
#include <stdio.h>
namespace yh
{
struct TreeNode
{
int data;
struct TreeNode* left;
struct TreeNode* right;
};
//这里只有声明
void function2();
}
3 std命名空间
std是c++标准库的命名空间.
c++担心别人写的内容和库里的内容出现命名冲突,
把标准库中所有的内容都放到了std这个命名空间中.
所以,要使用标准库中的内容,
不仅要包含对应的头文件,还要指定是std这个命名空间.
域的补充
1 对于变量和类型,默认先在局部域找,再到全局域找; 对于函数,默认在全局域找.
一般不会把类型声明写到函数里面
2 同一个域中不能定义同名的变量、结构体,不同域中可以定义同名的变量、结构体
c++输入/输出
c语言的printf和scanf继续可以使用,但它们都要指定输入输出的类型;
c++中可以使用cin进行输入,cout进行输出,可以自动识别类型.
#include <iostream>//cin和cout的头文件
using namespace std;
int main()
{
int val1;
double val2;
//cin 和cout可以自动识别类型
cin >> val1 >> val2;
cout << "hello " << val1 <<endl;//endl就相当于'\n'
cout << val2 << endl;
return 0;
}
1 cin和cout中的c其实就是console控制台,也就是运行程序时的黑框.
2 >> 叫做流提取运算符,cin >> val1 >> val2 就是提取控制台的输入到val1和val2中.
3 << 叫做流插入运算符,cout << val2 << endl就是把val2和换行的内容插入到控制台中.
缺省参数
什么是缺省参数
在声明或定义函数时,给函数的参数指定一个默认值(缺省值)
此时调用该函数:
如果没有指定参数,那参数就会采用该缺省值.
如果指定了参数,那缺省值不起作用.
这种参数就叫缺省参数.
怎么使用缺省参数
全缺省
函数的所有参数,全部给缺省值.
void test(int a = 1, int b = 2, int c = 3)
{
cout << a << b << c << endl;
}
int main()
{
test();
test(4);
test(4, 5);
test(4, 5, 6);
return 0;
}
注意:实参只能从左到右连续传,不能有间隔!!!
错误示范:
test( ,2, );
test(1, ,5);
test( , , 5);
半缺省
半缺省是指:缺省部分参数
void test1(int a, int b = 2, int c = 3)
{
cout << a << b << c << endl;
}
void test2(int a, int b, int c = 4)
{
cout << a << b << c << endl;
}
注意:形参只能从右往左连续缺省,不能有间隔!!!
此时,test1至少要传一个实参,test2至少要传两个实参.
用法举例
栈的初始化StackInit().
c语言中,栈的初始化一般是:
StackInit(struct Stack* ps)
里面的容量初始值是固定的.
例:#define INIT_CAPACITY 5
如果此时我同时需要两个栈:
A栈提前知道要插入100个数据.
B栈只需插入5个数据或者不清楚B栈要插入多少个数据.
那就有4种方案:
1 把初始容量扩大到100,此时的B栈会有一定程度的空间浪费.
2 初始容量不变,A栈需要慢慢扩容,扩容有一定销耗.
3 初始化多增加一个形参capacity,要求使用该函数时一定还要传个初始容量.
StackInit(struct Stack* ps, int capacity);
4 初始化多增加一个缺省参数capacity,视情况考虑是否需要传初始容量.
StackInit(struct Stack* ps, int capacity = 4);
若函数的声明和定义在同一文件中:
重要前提:当前文件的头文件展开以后
情况1:在当前文件,函数声明和定义同时出现缺省参数
无论参数的缺省值是否相同,无论声明和定义中的缺省参数数目是否相同.
都会报重定义默认的对应参数.
//test.h
#pragma once
#include <iostream>
void func(int a, int b, int c = 3);
//test.cpp
#include "test.h"//展开后相当于声明和定义在同一文件中
void func(int a = 1, int b = 2, int c = 3)
{
std::cout << a << b << c<< std::endl;
}
int main()
{
func();
}
情况2:在当前文件 只有声明有缺省参数 或 只有定义有缺省参数.
函数的传参以有缺省参数的为准.
//test.h
#pragma once
#include <iostream>
void func(int a, int b, int c);
//test.cpp
#include "test.h"
using namespace std;
void func(int a = 3, int b = 2, int c = 1)
{
cout << a << b << c << endl;
}
int main()
{
func();
}
声明给缺省,定义不给缺省就可以按声明传参.
若函数的声明和定义在不同文件中:
重要前提:当前文件的头文件展开以后
情况1:函数的声明和定义同时出现缺省参数.
不报错,该函数传参以当前文件为准:
如果当前文件有声明无定义,以声明为准.
如果当前文件无声明有定义,以定义为准.
为演示该情况,将使用两个.cpp文件.
//test.h
#pragma once
#include <iostream>
void func(int a, int b = 6, int c = 6);
void print();
//test2.cpp
#include <iostream>
void func(int a = 3, int b = 2, int c = 1)
{
std::cout << a << b << c << std::endl;
}
void print()
{
//当前文件只有func的定义,所以会打印321
func();
}
//test.cpp
#include "test.h"//相当于该文件只有func和print的声明
using namespace std;
int main()
{
func(1);//以声明为准
print();
return 0;
}
情况2:在当前文件 只有声明有缺省参数 或 只有定义有缺省参数.
该函数传参以当前文件为准:
如果当前文件有声明无定义,以声明为准.
如果当前文件无声明有定义,以定义为准.
//test.h
#pragma once
#include <iostream>
void func(int a, int b, int c);
void print();
//test2.cpp
#include <iostream>
void func(int a = 1, int b = 2, int c = 3)
{
std::cout << a << b << c << std::endl;
}
void print()
{
//当前文件只有定义,所以会打印123
func();
}
//test.cpp
#include "test.h"//相当于只有func的声明
using namespace std;
int main()
{
func(9, 9, 6);//以声明为准
print();
return 0;
}
缺省参数放在声明还是定义?
原因:
由于定义只有一份,不可能同时出现在多个文件;
而声明可以在多个文件中出现.
结果:
建议把缺省参数写到函数的声明中,而函数定义不写缺省参数.