目录
1. C++关键字(C++98)
命名冲突:
2. 命名空间:namespace关键字
2.1 命名空间定义
普通命名空间例子:
1.域作用限定符 ::
2.如何访问命名空间域内的变量:
3.不同命名空间中的同名变量如何使用:
4.命名空间的性质
细节1:这里n1是结构体,结构体已经升级成类,就可以不加struct
细节2:命名空间不影响声明周期,只是隔离了名字冲突,a还是全局变量
细节3:命名空间不能放在main函数中,只能放在全局
细节4:命名空间一般是放在头文件中
2.2 命名空间使用
1.using namespace byte ;
例子1:
例子2:using namespace byte: :cache
例子3:
例子4:
2.using namespace std;
3. C++输入&输出
(1.cin和cout讲解:
(2. C++输入输出更方便,
(3.输出支持空格 冒号分割
(4.想保留小数位数还是用c语言吧,他们输入输出可以混着用,c++也能控制但是很复杂,不建议学
4. 缺省参数/默认参数
4.1 缺省参数概念
4.2 缺省参数分类
(1.多个缺省参数:
(2.缺省参数的意义:
(3.总结:
5. 函数重载
5.1 函数重载概念+详解
为什么C语言不支持重载,C+ +支持重载?详解:
C++总计63个关键字,C语言32个关键字
ps:下面我们只是看一下C++有多少关键字,不对关键字进行具体的讲解。后面我们学到以后再细讲。
#includ
using namespace std; 又是什么东西呢,接下来由此引入命名冲突和命名空间关键字 namespace
命名冲突:同一个作用域不能定义同名变量 -- C语言没有很好的解决这个问题,CPP引入namespace解决这个问题
int rand = 0; int main() { printf("%d\n", rand); return 0; } 此时没问题,但是包含某些库的时候就会报错: #include
命名冲突 -- C语言没有很好的解决这个问题,CPP引入namespace解决这个问题
在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作
用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字
污染,namespace关键字的出现就是针对这种问题的
定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名
空间的成员。
(1)
此处打印的是函数地址,是stdlib.h中的一个创造随机值函数rand的地址
因为命名空间关键字namespace把变量rand=0关进了名叫bit的命名空间域中,隔离起来
(2)
#include
void f()
{}
int f = 0;
int main()
{
printf("%p\n", f);
}
————————这样写就不对,改成下面这样就对了:
#include
namespace bit // 命名空间域
{
int f = 0;
}
void f()
{}
int main()
{
printf("%p\n", f);
}
同名变量在不同域可以存在:
局部有限,则打印1,如果找不到局部变量才打印0
注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中
(1)情景1 ::a 左边域是空白(左边有没有空格无所谓),默认访问全局域的a
(2)情景2 假如一个命名空间叫bit,在其中定义了变量f和rand ,想用 f 或 rand ,就要展开bit写成 bit::f 和 bit::rand
(3)情景3 假如一个命名空间叫zsh,在其中定义了一个结构体类型 ListNode ,用这个类型定义变量
namespace zsh { int a = 0; void f() { printf("void f()\n"); } struct ListNode { int val; struct ListNode* next; }; } 可以这样定义变量: zsh::ListNode* n1=NULL ; //结构体指针 struct zsh::ListNode* n1 = NULL; zsh::ListNode n2 ; //结构体变量 struct zsh::ListNode n2;
#include
1、同名的命名空间是可以同时存在的,编译器编译时会合并
2、命名空间可以嵌套
3. 命名空间中的内容,既可以定义变量,也可以定义函数,可以定义类型(结构体类型)
(这里的n1是结构体,再使用时不用带前面的类型)
(左边是c++写法,右边是严格c语言写法)
命名空间中成员该如何使用呢?比如:
命名空间的使用有三种方式:
加命名空间名称及作用域限定符
使用using将命名空间中成员引入
使用using namespace 命名空间名称引入
//这里的意思是把byte这个命名空间定义的东西放出来,使namespace byte这个命名空间无效
前面已经有同名命名空间:
namespace byte
{
int b = 0;
namespace cache
{
struct Node
{
int val;
struct Node* next;
};
}
}
namespace byte
{
// a还是全局变量,命名空间不影响生命周期
int a = 0;
namespace data
{
struct Node
{
int val;
struct Node* next;
};
}
}
如果先展开data再展开byte就错了
只是展开了byte里的cache,那么可以把byte: :cache省了,原本是byte: :cache::Node n1 ,现在写成 struct Node n1 ;
但是不能只省byte ,写成struct cache: :Node n2; 就错了
如果都放出来,data和cache中都有struct Node 这个结构体,就又重名了,就错了
最规范的还是展开一个byte
直接using namespace bit; 会全部放出来,虽然能用f了,但是也把rand放出来了,变量rand和stdlib.h 中的函数rand又同名了,就错了
所以要用什么放什么:
单独放不用加namespace,这是语法规定
std是c++标准库的命名空间,#include 是c++头文件
using namespace std;就是展开c++头文件的内容
如果不写这句就需要指定命名空间std里的内容 std::cout
如果下面这样放开了std,再定义cout就和库里面的某些函数冲突了,一用cout就会报错
干脆不放开,然后指定使用命名空间std里面的内容,比如std::cout或者std::endl就可以使用
只放cout,指定不指定都可以
不能连着放
新生婴儿会以自己独特的方式向这个崭新的世界打招呼,C++刚出来后,也算是一个新事物,
那C++是否也应该向这个美好的世界来声问候呢?我们来看下C++是如何来实现问候的。
说明:
1. 使用cout标准输出(控制台)和cin标准输入(键盘)时,必须包含< iostream >头文件以及std标准命名空间。
注意:早期标准库将所有功能在全局域中实现,声明在.h后缀的头文件中,使用时只需包含对应头文件
即可,后来将其实现在std命名空间下,为了和C头文件区分,也为了正确使用命名空间,规定C++头文
件不带.h;旧编译器(vc 6.0)中还支持格式,后续编译器已不支持,因此推荐使用
+std的方式。
cin和cout看成控制台,>>流提取运算符和
cin>>a ,箭头方向由cin指向a,相当于cin内数据流入a
cout
endl相当于换行
不需增加数据格式控制,比如:整形--%d,字符--%c
一行输入多个,一行输出多个,且不用定义格式,自动控制输出
endl相当于换行,这里用endl和\n是一样的
(5.scanf和printf比cin和cout快一些,大量输出时有可能用,平时不考虑
大家知道什么是备胎吗?
C++中函数的参数也可以配备胎。
缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参。
这里的int a=0 就是缺省参数
全缺省:缺省参数给全了
多个缺省参数,只能传完第一个再传第二个,不能直接传第二个
半缺省:缺省参数给了一部分,即缺省部分
①最少传一个(第一个),不能一个不传
②半缺省 必须从右往左缺省,并且是连续的
必须从右往左缺省,并且是连续的例子:
struct Stack
{
int* a;
int size;
int capacity;
};
void StackInit(struct Stack* ps, int n = 4)
{
assert(ps);
ps->a = (int*)malloc(sizeof(int)*n);
ps->size = 0;
ps->capacity = n;
}
int main()
{
TestFunc(1);
TestFunc(1, 2);
TestFunc(1, 2, 3);
Stack st;
//StackInit(&st); //不给参数就是缺省参数,n=4
StackInit(&st, 100); //也可以给参数,n=100
return 0;
}
1. 半缺省参数必须从右往左依次来给出,不能间隔着给
2. 缺省参数不能在函数声明和定义中同时出现(函数声明和函数定义分离的情况)
(解释:怕你用不同的缺省参数
那该怎么给?缺省参数和声明在编译阶段处理!
声明定义都给缺省参数不行,存在歧义
声明不给定义给缺省参数不行,如果只给定义里面了,头文件#include "Queue.h" 在声明里面找不到缺省参数就报错,函数定义是在链接时连接起来的
只能声明给定义不给缺省参数,因为头文件#include "Queue.h" 展开,.cpp向上找会找到声明里面的缺省参数,)
3. 缺省值必须是常量或者全局变量
4. C语言不支持(编译器不支持)
自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重载了。
比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个是男足。前
者是“谁也赢不了!”,后者是“谁也赢不了!”
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题
例题:
A.int compare(double,double)
B.double compare(double,double)
C.double compare(double,int)
D.int compare(int,int)
只要参数列表不一样,就构成函数重载,ABC都对,D函数重载不能依靠返回值的不同来构成重载,因为调用时无法根据参数列表确定调用哪个重载函 数,故错
正确例子:
错误例子:
正确:
正确:
// 顺序不同
void f(int i, double d)
{
cout << "void f(int i, double d)" << endl;
}
void f(double i, int d)
{
cout << "void f(double i, int d)" << endl;
}
int main()
{
f(1, 22.2);
f(22.2, 1);
}
意义:使用相同功能的函数时,如果类型不同可以使用函数重载
void swap(int* p1, int* p2)
{
int x = *p1;
*p1 = *p2;
*p2 = x;
}
void swap(double* p1, double* p2)
{
double x = *p1;
*p1 = *p2;
*p2 = x;
}
int main()
{
int a = 0, b = 1;
double c = 1.1, d = 2.2;
swap(&a, &b);
swap(&c, &d);
return 0;
}
举例说明:有三个文件头文件 f.h 和 c++文件 f.cpp , test.cpp
// f.h
#include
void f(int a,double d);
void f(double d,int a);
// f.cpp
#include"f.h"
void f(int a, double b)
{
printf("%d, %f\n", a, b);
}
void f(double b,int a)
{
printf("%f, %d\n",b,a) ;
}
// test.cpp
#include"f.h"
int main( )
{
f(1,2.222) ;
f(2.222, 1) ;
return 0;
}
编译链接过程:
1.预处理:预处理会把头文件展开到cpp文件中,f.h就没了,同时宏替换,条件编译,去注释,f.cpp 和test.cpp变为f.i 和 test.i 文件
2.编译:检查语法,生成汇编代码,f.i 和 test.i 文件 转化成 f.s 和 test.s文件
3.汇编:把汇编代码转换成二进制的机器码,f.s 和 test.s文件 转化成 f.o 和 test.o文件
4.链接:找调用函数的地址,链接对应上,合并到一起,把f.o 和 test.o合并成a.out。
汇编后:.o文件中有 函数调用指令 和 符号表
符号表:记录了函数定义和函数名的和函数地址映射关系 还有其他的东西暂时先不用管
main函数调用指令中去调用两个 f函数 时,会有一句指令叫call 函数名(函数地址),还未链接时,test.cpp中没有函数定义,call指令只需要找函数声明,声明是一种承诺,承诺函数定义在别的文件中,有了"承诺"后call指令可以先不知道函数地址,随后进行链接,链接是把 f.o 和 test.o 里面所有的指令链接起来成为一个文件叫 a.out ,此时兑现"承诺",call指令通过函数的名字去上面的符号表里面找到函数地址把“?未知地址”添上函数地址才能成功执行call指令(符号表汇总了函数名的和函数地址映射关系),但是:c语言中call后面的函数名就是原来的函数名,没有修改,没有区别,如果函数名相同,再去找函数地址就会出问题,因为无法区分两个同名函数。c++正是对函数名做了修饰进行区分才能够进行函数重载:
函数名不再是原本的函数名了,而是:_Z 函数名长度 函数名 类型首字母 的形式进行记录函数,c++把参数类型首字符带进命名规则中去了,因此去找函数地址就可以区分同名函数,这也说明了为什么函数重载需要 形参个数 或 类型 或 顺序 不同了。