问:c++共有多少个关键字?
答:在c++98版本 一共有63个关键字
使用命名空间的目的是:对标识符的名称进行本地化,以避免命名冲突或名字污染; 比方说:正在读博客的你和我一起写个项目工程,但是我们写的代码可能会有一些重复的定义变量名/类名/函数名,我定义一个变量 a ;可能你也定义一个变量 a ,所以就可能会发生冲突;而且我们自己写代码的时候也可能会发生相同的定义名称,这时候你就很为难编译器,因为它不知道哪一个是对的;
下面是:命名空间的定义和使用,相信一看代码就懂~~!!
#include
#include
namespace A
{
int a = 10;
}
namespace B
{
int a = 20;
}
int a = 30;
int main()
{
printf("%d\n", A::a);
printf("%d\n", B::a);
printf("%d\n", a);
system("pause");
return 0;
}
接下来简要总结命名空间的使用~
(1)使用 using A::a ; 仅仅使用A空间的 a
(2)使用 using namespace A ;使用A空间的所有对象
点它==》命名空间的注意事项和常见的错误
先看一段代码,打印的结果是 20 10
void print(int a = 10)
{
printf("%d ", a);
}
int main()
{
print(20);
print();
system("pause");
return 0;
}
很明显如果用户指定参数的话,则打印的是指定参数,否则则打印默认参数,定义函数里的参数可以形象的理解为备胎
看似简单的概念,背后往往有我们看不见的坑,如果不注意就会踩~
(1)参数不可以随便无顺序的指定,比方说:
void func(int a = 10, int b; int c = 20);
这样就错了,因为半缺省参数必须从右往左依次来给出,不能间隔着给
(2) 缺省参数不能在函数声明和定义中同时出现;
为什么?因为如果两个地方都有,害怕你定义和声明的参数值不同,这时候编译器将无法确定使用哪个备胎(默认参数值)
错误是:重定义默认参数,意思就是重复定义了相同的值;
(3)缺省值必须是常量或者全局变量
(4)C语言不支持(编译器不支持)
C语言为什么不支持?这就引申出下一个概念《函数重载》
这是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 顺序) 必须 不同,常用来处理实现功能类似数据类型不同的问题;总结起来就是:如果两个函数的 函数名相同 && (参数个数 || 参数类型不同 || 参数顺序 );这就是函数重载;
注意一点,函数重载和函数的返回值类型没有关系…
也就是说仅仅是返回值类型不同是不能构成函数重载滴~!!
why? why? why?我们先可以回顾一下C语言下是怎么回事?
void func1(int a);
int main()
{
func1(10);
system("pause");
return 0;
}
这个编译的时候会通过吗?
回答 ‘会通过’ 的老铁,怕是你上 C 语言的时候偷偷打游戏了吧~~哈
这个是不会通过的,我们先看看出错原因?
说的是:编译器识别不了函数名,可是明明在代码里有这个 func1 函数名的啊,大家要看仔细点,那个是函数声明,不是函数定义《声明和定义区别》
所以编译器不会识别函数名,编译器找不到 _func1 这个符号,这个符号就是编译器经过汇编之后转换的名称,C语言的名字修饰规则非常简单,只是在函数名字前面添加了下划线。可是这和C++函数重载有啥关系?当然有关系。。。不信你看下面
我们再看看 C++语言里的汇编代码
我们发现C++的函数修饰非常复杂,如果我们再仔细看,会发现一些乱码 “?func1@@YAXH@Z” 这又是啥?这个东西就是编译器在汇编的时候将函数声明那一行的代码所转换的二进制代码,简单来说就是编译器将函数、变量的名称,变量顺序,参数个数,重新改编的机制;目的当然是为了区分函数;但是C语言在编译的时候根本就不管 形参类型和个数,但是C++ 却把它弄得很严格;
总结起来就是:c++ 只要两个函数的参数类型/个数/顺序不同,编译器就可以区分开来,但是C语言无法区分,因为C语言在编译的时候不管参数类型,这样就导致了如果有两个函数名相同的函数,C语言下编译器是无法识别的~~
这也就是为什么C语言不支持函数重载的原因!!
更加详细的解释:函数重载 ;c/c++调用约定
C++ 底层在函数编译的时候会进行重命名机制,这个重命名会因为函数参数不同和参数的顺序不同,最终生成的编译符号也不一样,由于不同的编译器规则是不一样的,但是这不重要,只要能生成唯一的符号就行了,
关于引用的概念也特别好理解,就是取一个“别名”,比如你叫 “张三”,但是在家还有一个乳名叫 “蛋蛋”,而蛋蛋这个就是引用;引用不开辟新的空间,他和引用的对象共用一块内存空间;就像 “张三" 和 “蛋蛋” 指的都是同一个人!!
为什么要有这个概念?存在的作用是什么?后面讲!!
那该如何定义?
类型名& 引用变量名(蛋蛋) = 实体(张三);
注意:引用变量名的对象类型必须和实体是同种类型的;
//还有其他什么特点吗?
===下面是使用引用的场景!!!c++的大佬定义出来这个引用,总得要做点事情吧,不然定义出来干嘛尼!!~~
1.作为参数,,热身热身,先写一段代码
#include
#include
using namespace std;
void swap(int& a, int& b)
{
int tem = a;
a = b;
b = tem;
}
int main()
{
int aa = 10;
int bb = 20;
cout << aa << " " << bb << endl;
swap(aa, bb);
cout << aa << " " << bb << endl;
system("pause");
return 0;
}
由于操作形参就是在操作实参,指的是同一个对象,所以会改变aa,bb的值
来点难度!!~
int& Add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int& ret = Add(1, 2);
Add(3, 4);
cout << "Add(1, 2) is :" << ret << endl;
system("pause");
return 0;
}
打印结果是多少?答:7
ret 明明引用的是Add(1,2),为什么会变为Add(3,4);
个人觉得:由于c在栈开辟的空间,所以在作为返回值的时候会随着函数的调用结束而被系统回收;这时候编译器会给一个警告!
注意:如果函数返回时,离开函数作用域后,其栈上空间已经还给系统,因此不能用栈上的空间作为引用类型返回。如果以引用类型返回,返回值的生命周期必须不受函数的限制(即比函数生命周期长)。
在前面加一个"static" 关键字 =》 static int c = a + b;
这样 c 就不会随着函数结束而被释放;
之前我们提到为什么要使用引用,当然是它的效率高,比方说:在传参数的时候
这里我们使用 clock
这个函数对时间做记录,单位是毫秒ms
#include
#include
#include
using namespace std;
struct A
{
int a[10000];
};
void func1(A& a){}
void func2(A a){}
void test()
{
A a;
int max = 10000;
size_t begin = clock();
for (int i = 0; i < max; ++i)
{
func1(a);
}
size_t end = clock();
size_t begin2 = clock();
for (int i = 0; i < max; ++i)
{
func2(a);
}
size_t end2 = clock();
cout << "传引用所用的时间为:" << end - begin << endl;
cout << "传值所用的时间为 :" << end2 - begin2 << endl;
}
int main()
{
for (int i = 0; i < 5; ++i)
{
test();
}
system("pause");
return 0;
}
从结果我们可以看到差别还是很大的,特别是在做一些大型的数据传输的时候,
我们再来看一下他们分别作为返回值类型的效率
struct {int a[10000];};
A a;
A& func1(){return a;}
A func2(){return a;}
int main(){
size_t begin1 = clock();
for (int i = 0; i < 1000; ++i){
func1();
}
size_t end1 = clock();
size_t begin2 = clock();
for (int i = 0; i < 1000; ++i){
func2();
}
size_t end2 = clock();
cout <<"传引用:"<<end1 - begin1 << endl;
cout <<"传值 :"<<end2 - begin2 << endl;
system("pause");
return 0;
}
这个结果可是差的可是特别大了
为什么值和引用的效率会差的如此之大,是因为不管在传参的时候还是在作为返回值类型的时候,‘值’ 它总是先要进行拷贝,也就是做一个副本,而不是直接对实体进行操作,所以会花费很大的时间,这就是我们有时候在电脑里移动一个文件的时候感觉很快,但是拷贝的时候就很慢;
引用 vs 指针 之间的区别?
引用在定义时必须初始化,指针没有要求
引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
没有NULL引用,但有NULL指针
在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
有多级指针,但是没有多级引用
访问实体方式不同,指针需要显式解引用,引用编译器自己处理
引用比指针使用起来相对更安全
内联函数是什么?
内联函数的特点是什么?
为什么要使用内联函数?
宏 vs 内联函数
宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。
增加代码的复用性;提高性能
可读性差;没有类型安全检查;不方便调试;可维护性差;
内联函数:
节省了变量在栈上的开销和函数返回/调用的时间开销,内联函数没有普通函数调用时的额外开销(压栈,跳转,返回)
如果函数的代码较长,使用内联将消耗过多内存,如果函数体内有循环,那么执行函数代码时间比调用开销大。
内联函数的使用场景 和 注意事项?
点它==》深入理解内联函数
auto的用法还是比较多的,先做一个简答的总结,后序再继续更新
https://www.cnblogs.com/mrlsx/p/5510496.html
这是个很有意思的事情,而且用起来也很方便,先看一段代码
int main()
{
int a[10] = { 1,2,3,4,5,6,7,8,9,0 };
for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
{
cout << a[i] <<" ";
}
cout << endl;
for (int* p = a; p < a + sizeof(a) / sizeof(a[0]); ++p)
{
cout << *p <<" ";
}
system("pause");
return 0;
}
这两种方法是我们平时所用遍历数组元素,但是有一个缺陷就是,在遍历的时候必须要计算出数组范围,如果算错还可能导致越界;所以C++ 11 提出了一个更加简洁的办法就是~~~
范围for循环的用法 :for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围
int a[10] = { 1,2,3,4,5,6,7,8,9,0 };
for (int e : a)
{
cout << e << " ";
}
稍微注意一点就是:被迭代的范围必须要是确定的,如果被迭代的作为参数传到函数的话,就会报错,因为范围不确定,,比如:
void TestFor(int a[])
{
for(int& e : a)
cout<< e <<endl;
}
学习c++是一个很漫长的过程,让我们一起加油!!~~~