1.C/C++关键字
1.1static(静态)变量
在C中,关键字static是静态变量:
静态变量只会初始化一次,然后在这函数被调用过程中值不变。
在文件内定义静态变量(函数外),作用域是当前文件,该变量可以被文件内所有函数访问,不能被其他文件函数访问。为本地的全局变量,只初始化一次。
在C++中,类内数据成员可以定义为static
对于非静态数据成员,每个对象有一个副本。而静态数据成员是类的成员,只存在一个副本,被所有对象共享。
静态成员变量没有实例化对象也可以使用,“类名:静态成员变量”
静态成员变量初始化在类外,但是private和protected修饰的静态成员不能类外访问。
classStu
{
public:
staticintage;
private:
staticintheight;
};
//初始化静态成员变量
intStu::age=19;
intStu::height=;
intmain()
{
coutStu::ageendl;//输出19;
coutStu::heightendl;//错误的,私有无法访问。
Stus;
couts::ageendl;//输出19;
couts::heightendl;//错误的,私有无法访问。
return0;
}
在类中,static修饰的函数是静态成员函数。静态成员函数一样属于类,不属于对象,被对象共享。静态成员函数没有this指针,不能访问非静态的函数和变量,只能访问静态的。
与全局变量相比,静态数据成员的优势:
全局变量作用域是整个工程,而static作用域是当前文件,避免命名冲突
静态数据成员可以是private成员,而全局变量不能,实现信息隐藏
为什么静态成员变量不能在类内初始化?
因为类的声明可能会在多处引用,每次引用都会初始化一次,分配一次空间。这和静态变量只能初始化一次,只有一个副本冲突,因此静态成员变量只能类外初始化。
为什么static静态变量只能初始化一次?
所有变量都只初始化一次。但是静态变量在全局区(静态区),而自动变量在栈区。静态变量生命周期和程序一样,只创建初始化一次就一直存在,不会销毁。而自动变量生命周期和函数一样,函数调用就进行创建初始化,函数结束就销毁,所以每一次调用函数就初始化一次。
在头文件中定义静态变量是否可行?
不可行,在头文件中定义的一个static变量,对于包含该头文件的所有源文件,实质上在每个源文件内定义了一个同名的static变量。造成资源浪费,可能引起bug
1.2const的作用
常量类型也称为const类型,使用const修饰变量或者对象
在C中,const的作用为:
定义变量(局部或者全局)为常量
constinta=10;//常量定义时,必须初始化
1
修饰函数的参数,函数体内不能修改这个参数的值
修饰函数的返回值
const修饰的返回值类型为指针,返回的指针不能被修改,而且只能符给被const修饰的指针
constchar*GetString()
{
//...
}
intmain()
{
char*str=GetString();//错误,str没被const修饰
constchar*str=GetString();//正确
}
const修饰的返回值类型为引用,那么函数调用表达式不能做左值(函数不能被赋值)
constintadd(inta,intb)
{
//..
}
intmain()
{
add(a,b)=4;//错误,const修饰add的返回引用,不能做左值
}
const修饰的返回值类型为普通变量,由于返回是普通临时变量,const修饰没意义。
在c++中,const还有作用为:
const修饰类内的数据成员。表示这个数据成员在某个对象的生命周期是常量,不同对象的值可以不一样,因此const成员函数不能在类内初始化。
const修饰类内的成员函数。那么这个函数就不能修改对象的成员变量
const的优点?
进行类型检查,使编译器对处理内容有更多了解。
避免意义模糊的数字出现,类似宏定义,方便对参数进行修改。
保护被修饰的内容,防止被意外修改
为函数重载提供参考
classA
{
voidf(inti){...}//非const对象调用
voidf(inti)const{...}//const对象调用
}
5.节省内存
6.提高程序效率(编译器不为普通const常量分配存储空间,而保存在符号表中。称为一个编译期间的常量,没有存储和读内存的操作)
什么时候使用const?
修饰一般常量
修饰对象
修饰常指针
constint*p;
intconst*p;
int*constp;
constint*constp;
修饰常引用
修饰函数的参数
修饰函数返回值
修饰类的成员函数
修饰另一文件中引用的变量
externconstintj;
const和指针(常量指针、指针常量)
常量指针(const修饰常量,const在*的左边)
constint*p=a;//const修饰int,指针的指向可以修改,但是指针指向的值不能改
intconst*p;//同上
p=b;//正确
*p=10;//错误
指针常量(const修饰指针,const在*的右边)
int*constp=a;//const修饰指针,指针的指向不可以改,但是指针指向的值可以改
*p=10;//正确
p=b;//错误
const都修饰指针和常量(指针和常量都不能修改)
constint*constp;
intconst*constp;
1.3switch语句中case结尾是否必须加break
**一般必须在case结尾加break。**因为通过switch确认入口点,一直往下执行,直到遇见break。否则会执行完这个case后执行后面的case,default也会执行。注,switch(c),c可以是int、long、char等,但是不能是float
1.4volatile的作用
volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。
编译器不再进行优化,从而可以提供对特殊地址的稳定访问。
系统总是重新从它所在的内存读取数据,不会利用cache中原有的数值。
用于多线程被多个任务共享的变量,或者并行设备的硬件寄存器
1.5断言ASSERT()是什么?
**是一个调试程序使用的宏。**定义在assert.h中,用于判断是否出现非法数据。括号内的值为false(0),程序报错,终止运行。
ASSERT(n!=0);//n为0的时候程序报错
k=10/n;
ASSERT()在Debug中有,在Release中被忽略。ASSERT()是宏,assert()是ANSCI标准中的函数,但是影响程序性能。
1.6枚举变量的值计算
#includestdio.h
intmain()
{
enum{a,b=5,c,d=4,e};
printf("%d%d%d%d%d",a,b,c,d,e);
return0;
}
输出为
1.7字符串存储方式
字符串存储在栈中
charstr1[]="abc";
charstr2[]="abc";
字符串存储在常量区
char*str3="abc";
char*str4="abc";
字符串存储在堆中
char*str5=(char*)malloc(4);
strcpy(str5,"abc");
char*str6=(char*)malloc(4);
strcpy(str6,"abc");
字符串是否相等
str1!=str2,str1和str2是两个字符串的首地址。
str3==srt4,str3和str4是常量的地址,同样字符串在常量区只存在一份。
str5!=str6,str5和str6是指向堆的地址。
1.8程序内存分区
内存高地址栈区
堆区
全局/静态区(.bss段.date段)
常量区
内存低地址代码区
栈区(stack)
临时创建的局部变量存放在栈区。
函数调用时,其入口参数存放在栈区。
函数返回时,其返回值存放在栈区。
const定义的局部变量存放在栈区。
堆区(heap)
堆区用于存放程序运行中被动态分布的内存段,可增可减。
malloc函数分布的内存,必须用free进行内存释放,否则会造成内存泄漏。
全局区(静态区)
(c语言中)全局区有.bss段和.data段组成,可读可写。
C++不分bss和data
.bss段
未初始化的全局变量存放在.bss段。
初始化为0的全局变量和初始化为0的静态变量存放在.bss段。
.bss段不占用可执行文件空间,其内容有操作系统初始化。
.data段
已经初始化的全局变量存放在.data段。
静态变量存放在.data段。
.data段占用可执行文件空间,其内容有程序初始化。
const定义的全局变量存放在.rodata段。
常量区
字符串存放在常量区。
常量区的内容不可以被修改。
代码区
程序执行代码(二进制代码文件)存放在代码区。
1.9*p++和(*p)++的区别
*p++先完成取地址,然后对指针地址进行++,再取值
(*p)++,先完成取值,再对值进行++
1.10new/delete与malloc/free的异同
相同点
都可用于内存的动态申请和释放
不同点
new/delete是C++运算符,malloc/free是C/C++语言标准库函数
new自动计算要分配的空间大小,malloc需要手工计算
new是类型安全的,malloc不是。例如:
int*p=newfloat[2];//编译错误
int*p=(int*)malloc(2*sizeof(double));//编译无错误
new调用名为operatornew的标准库函数分配足够空间并调用相关对象的构造函数,delete对指针所指对象运行适当的析构函数;然后通过调用名为operatordelete的标准库函数释放该对象所用内存。malloc/free均没有相关调用
malloc/free需要库文件支持,new/delete不用
new是封装了malloc,直接free不会报错,但是这只是释放内存,而不会析构对象
1.11exit()和return的区别
return是语言级的,标志调用堆栈的返回。是从当前函数的返回,main()中return的退出程序
exit()是函数,强行退出程序,并返回值给系统
return实现函数逻辑,函数的输出。exit()只用来退出。
1.12extern和export的作用
变量的声明有两种情况:
一种是需要建立存储空间的。例如:inta在定义的时候就已经建立了存储空间。
另一种是不需要建立存储空间的。例如:externinta其中变量a是在别的文件中定义的。
总之就是:把建立空间的声明成为“定义”,把不需要建立存储空间的成为“声明”。
extern
普通变量、类。结构体
export(C++中新增)
和exturn类似,但是用作模板
使用该关键字可实现模板函数的外部调用
模板实现的时候前面加上export,别的文件包含头文件就可用该模板
1.13C++中,explicit的作用
隐式转换
Strings1="hello";
//进行隐式转换,等价于
Strings1=String("hello");
1
2
3
explicit阻止隐式转换
classTest1
{
public:
Test1(intn){num=n}
private:
intnum;
}
classTest2
{
public:
explicitTest2(intn){num=n}
private:
intnum;
}
intmain()
{
Test1t1=1;//正确,隐式转换
Test2t2=1;//错误,禁止隐式转换
Test2t2(1);//正确,可与显示调用
}
1.14C++的异常处理
C++中的异常处理机制主要使用try、throw和catch三个关键字
#includeiostream
usingnamespacestd;
intmain()
{
doublem=1,n=0;
try{
cout"beforedividing."endl;
if(n==0)
throw-1;//抛出int型异常
elseif(m==0)
throw-1.0;//拋出double型异常
else
coutm/nendl;
cout"afterdividing."endl;
}
catch(doubled){
cout"catch(double)"dendl;
}
catch(...){
cout"catch(...)"endl;
}
cout"finished"endl;
return0;
}
//运行结果
//beforedividing.
//catch(...)
//finished
代码中,对两个数进行除法计算,其中除数为0。可以看到以上三个关键字,
程序的执行流程是先执行try包裹的语句块,如果执行过程中没有异常发生,则不会进入任何catch包裹的语句块,如果发生异常,则使用throw进行异常抛出,再由catch进行捕获,
throw可以抛出各种数据类型的信息,代码中使用的是数字,也可以自定义异常class。catch根据throw抛出的数据类型进行精确捕获(不会出现类型转换),如果匹配不到就直接报错,可以使用catch(…)的方式捕获任何异常(不推荐)。
当然,如果catch了异常,当前函数如果不进行处理,或者已经处理了想通知上一层的调用者,可以在catch里面再throw异常。
1.15回调函数
把一段可执行的代码像参数传递那样传给其他代码,而这段代码会在某个时刻被调用执行,这就叫做回调。
如果代码立即被执行就称为同步回调,如果过后再执行,则称之为异步回调。
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
主函数和回调函数是在同一层的,而库函数在另外一层。如果库函数对我们不可见,我们修改不了库函数的实现,也就是说不能通过修改库函数让库函数调用普通函数那样实现,那我们就只能通过传入不同的回调函数
sort(),中自定义的cmp就是回调函数
————————————————
版权声明:本文为CSDN博主「songwei」的原创文章,遵循CC4.0BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接: