C语言与C++面试知识总结.docx
- 文档编号:9052856
- 上传时间:2023-02-03
- 格式:DOCX
- 页数:32
- 大小:32.39KB
C语言与C++面试知识总结.docx
《C语言与C++面试知识总结.docx》由会员分享,可在线阅读,更多相关《C语言与C++面试知识总结.docx(32页珍藏版)》请在冰豆网上搜索。
C语言与C++面试知识总结
const
作用
1.修饰变量,说明该变量不可以被改变;
2.修饰指针,分为指向常量的指针(pointertoconst)和自身是常量的指针(常量指针,constpointer);
3.修饰引用,指向常量的引用(referencetoconst),用于形参类型,即避免了拷贝,又避免了函数对值的修改;
4.修饰成员函数,说明该成员函数内不能修改成员变量。
const的指针与引用
1.指针
∙指向常量的指针(pointertoconst)
∙自身是常量的指针(常量指针,constpointer)
2.引用
∙指向常量的引用(referencetoconst)
∙没有constreference,因为引用本身就是constpointer
(为了方便记忆可以想成)被const修饰(在const后面)的值不可改变,如下文使用例子中的p2、p3。
使用
//类
classA
{
private:
constinta;//常对象成员,只能在初始化列表赋值
public:
//构造函数
A():
a(0){};
A(intx):
a(x){};//初始化列表
//const可用于对重载函数的区分
intgetValue();//普通成员函数
intgetValue()const;//常成员函数,不得修改类中的任何数据成员的值
};
voidfunction()
{
//对象
Ab;//普通对象,可以调用全部成员函数、更新常成员变量
constAa;//常对象,只能调用常成员函数
constA*p=&a;//指针变量,指向常对象
constA&q=a;//指向常对象的引用
//指针
chargreeting[]="Hello";
char*p1=greeting;//指针变量,指向字符数组变量
constchar*p2=greeting;//指针变量,指向字符数组常量(const后面是char,说明指向的字符(char)不可改变)
char*constp3=greeting;//自身是常量的指针,指向字符数组变量(const后面是p3,说明p3指针自身不可改变)
constchar*constp4=greeting;//自身是常量的指针,指向字符数组常量
}
//函数
voidfunction1(constintVar);//传递过来的参数在函数内不可变
voidfunction2(constchar*Var);//参数指针所指内容为常量
voidfunction3(char*constVar);//参数指针为常量
voidfunction4(constint&Var);//引用参数在函数内为常量
//函数返回值
constintfunction5();//返回一个常数
constint*function6();//返回一个指向常量的指针变量,使用:
constint*p=function6();
int*constfunction7();//返回一个指向变量的常指针,使用:
int*constp=function7();
static
作用
1.修饰普通变量,修改变量的存储区域和生命周期,使变量存储在静态区,在main函数运行前就分配了空间,如果有初始值就用初始值初始化它,如果没有初始值系统用默认值初始化它。
2.修饰普通函数,表明函数的作用范围,仅在定义该函数的文件内才能使用。
在多人开发项目时,为了防止与他人命名空间里的函数重名,可以将函数定位为static。
3.修饰成员变量,修饰成员变量使所有的对象只保存一个该变量,而且不需要生成对象就可以访问该成员。
4.修饰成员函数,修饰成员函数使得不需要生成对象就可以访问该函数,但是在static函数内不能访问非静态成员。
this指针
1.this指针是一个隐含于每一个非静态成员函数中的特殊指针。
它指向调用该成员函数的那个对象。
2.当对一个对象调用成员函数时,编译程序先将对象的地址赋给this指针,然后调用成员函数,每次成员函数存取数据成员时,都隐式使用this指针。
3.当一个成员函数被调用时,自动向它传递一个隐含的参数,该参数是一个指向这个成员函数所在的对象的指针。
4.this指针被隐含地声明为:
ClassName constthis,这意味着不能给this指针赋值;在ClassName类的const成员函数中,this指针的类型为:
constClassName const,这说明不能对this指针所指向的这种对象是不可修改的(即不能对这种对象的数据成员进行赋值操作);
5.this并不是一个常规变量,而是个右值,所以不能取得this的地址(不能&this)。
在以下场景中,经常需要显式引用this指针:
∙为实现对象的链式引用;
∙为避免对同一对象进行赋值操作;
∙在实现一些数据结构时,如list。
inline内联函数
特征
∙相当于把内联函数里面的内容写在调用内联函数处;
∙相当于不用执行进入函数的步骤,直接执行函数体;
∙相当于宏,却比宏多了类型检查,真正具有函数特性;
∙编译器一般不内联包含循环、递归、switch等复杂操作的内联函数;
∙在类声明中定义的函数,除了虚函数的其他函数都会自动隐式地当成内联函数。
使用
inline使用
//声明1(加inline,建议使用)
inlineintfunctionName(intfirst,intsecond,...);
//声明2(不加inline)
intfunctionName(intfirst,intsecond,...);
//定义
inlineintfunctionName(intfirst,intsecond,...){/****/};
//类内定义,隐式内联
classA{
intdoA(){return0;}//隐式内联
}
//类外定义,需要显式内联
classA{
intdoA();
}
inlineintA:
:
doA(){return0;}//需要显式内联
编译器对inline函数处理步骤
1.将inline函数体复制到inline函数调用点处;
2.为所用inline函数中的局部变量分配内存空间;
3.将inline函数的的输入参数和返回值映射到调用方法的局部变量空间中;
4.如果inline函数有多个返回点,将其转变为inline函数代码块末尾的分支(使用GOTO)。
优缺点
优点
∙内联函数同宏函数一样将在被调用处进行代码展开,省去了参数压栈、栈帧开辟与回收,结果返回等,从而提高程序运行速度。
∙内联函数相比宏函数来说,在代码展开时,会做安全检查或自动类型转换(同普通函数),而宏定义则不会。
∙在类中声明同时定义的成员函数,自动转化为内联函数,因此内联函数可以访问类的成员变量,宏定义则不能。
∙内联函数在运行时可调试,而宏定义不可以。
虚函数(virtual)可以是内联函数(inline)吗?
Are"inlinevirtual"memberfunctionseveractually"inlined"?
∙虚函数可以是内联函数,内联是可以修饰虚函数的,但是当虚函数表现多态性的时候不能内联。
∙内联是在编译器建议编译器内联,而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因此虚函数表现为多态性时(运行期)不可以内联。
∙inlinevirtual唯一可以内联的时候是:
编译器知道所调用的对象是哪个类(如Base:
:
who()),这只有在编译器具有实际对象而不是对象的指针或引用时才会发生。
虚函数内联使用
#include
usingnamespacestd;
classBase
{
public:
inlinevirtualvoidwho()
{
cout<<"IamBase\n";
}
virtual~Base(){}
};
classDerived:
publicBase
{
public:
inlinevoidwho()//不写inline时隐式内联
{
cout<<"IamDerived\n";
}
};
intmain()
{
//此处的虚函数who(),是通过类(Base)的具体对象(b)来调用的,编译期间就能确定了,所以它可以是内联的,但最终是否内联取决于编译器。
Baseb;
b.who();
//此处的虚函数是通过指针调用的,呈现多态性,需要在运行时期间才能确定,所以不能为内联。
Base*ptr=newDerived();
ptr->who();
//因为Base有虚析构函数(virtual~Base(){}),所以delete时,会先调用派生类(Derived)析构函数,再调用基类(Base)析构函数,防止内存泄漏。
deleteptr;
ptr=nullptr;
system("pause");
return0;
}
volatile
volatileinti=10;
∙volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素(操作系统、硬件、其它线程等)更改。
所以使用volatile告诉编译器不应对这样的对象进行优化。
∙volatile关键字声明的变量,每次访问时都必须从内存中取出值(没有被volatile修饰的变量,可能由于编译器的优化,从CPU寄存器中取值)
∙const可以是volatile(如只读的状态寄存器)
∙指针可以是volatile
assert()
断言,是宏,而非函数。
assert宏的原型定义在
可以通过定义NDEBUG来关闭assert,但是需要在源代码的开头,include
assert()使用
#defineNDEBUG//加上这行,则assert不可用
#include
assert(p!
=NULL);//assert不可用
sizeof()
∙sizeof对数组,得到整个数组所占空间大小。
∙sizeof对指针,得到指针本身所占空间大小。
#pragmapack(n)
设定结构体、联合以及类成员变量以n字节方式对齐
#pragmapack(n)使用
#pragmapack(push)//保存对齐状态
#pragmapack(4)//设定为4字节对齐
structtest
{
charm1;
doublem4;
intm3;
};
#pragmapack(pop)//恢复对齐状态
位域
Bitmode:
2;//mode占2位
类可以将其(非静态)数据成员定义为位域(bit-field),在一个位域中含有一定数量的二进制位。
当一个程序需要向其他程序或硬件设备传递二进制数据时,通常会用到位域。
∙位域在内存中的布局是与机器有关的
∙位域的类型必须是整型或枚举类型,带符号类型中的位域的行为将因具体实现而定
∙取地址运算符(&)不能作用于位域,任何指针都无法指向类的位域
extern"C"
∙被extern限定的函数或变量是extern类型的
∙被extern"C"修饰的变量和函数是按照C语言方式编译和链接的
extern"C"的作用是让C++编译器将extern"C"声明的代码当作C语言代码处理,可以避免C++因符号修饰导致代码不能和C语言库中的符号进行链接的问题。
extern"C"使用
#ifdef__cplusplus
extern"C"{
#endif
void*memset(void*,int,size_t);
#ifdef__cplusplus
}
#endif
struct和typedefstruct
C中
//c
typedefstructStudent{
intage;
}S;
等价于
//c
structStudent{
intage;
};
typedefstructStudentS;此时S等价于structStudent,但两个标识符名称空间不相同。
另外还可以定义与structStudent不冲突的voidStudent(){}。
C++中
由于编译器定位符号的规则(搜索规则)改变,导致不同于C语言。
1.如果在类标识符空间定义了structStudent{...};,使用Studentme;时,编译器将搜索全局标识符表,Student未找到,则在类标识符内搜索。
即表现为可以使用Student也可以使用structStudent,如下:
//cpp
structStudent{
intage;
};
voidf(Studentme);//正确,"struct"关键字可省略
2.若定义了与Student同名函数之后,则Student只代表函数,不代表结构体,如下:
typedefstructStudent{
intage;
}S;
voidStudent(){}//正确,定义后"Student"只代表此函数
//voidS(){}//错误,符号"S"已经被定义为一个"structStudent"的别名
intmain(){
Student();
structStudentme;//或者"Sme";
return0;
}
C++中struct和class
总的来说,struct更适合看成是一个数据结构的实现体,class更适合看成是一个对象的实现体。
区别:
最本质的一个区别就是默认的访问控制
∙默认的继承访问权限。
struct是public的,class是private的。
∙struct作为数据结构的实现体,它默认的数据访问控制是public的,而class作为对象的实现体,它默认的成员变量访问控制是private的。
union联合
联合(union)是一种节省空间的特殊的类,一个union可以有多个数据成员,但是在任意时刻只有一个数据成员可以有值。
当某个成员被赋值后其他成员变为未定义状态。
联合有如下特点:
∙默认访问控制符为public
∙可以含有构造函数、析构函数
∙不能含有引用类型的成员
∙不能继承自其他类,不能作为基类
∙不能含有虚函数
∙匿名union在定义所在作用域可直接访问union成员
∙匿名union不能包含protected成员或private成员
∙全局匿名联合必须是静态(static)的
union使用
#include
unionUnionTest{
UnionTest():
i(10){};
inti;
doubled;
};
staticunion{
inti;
doubled;
};
intmain(){
UnionTestu;
union{
inti;
doubled;
};
std:
:
cout< : endl;//输出UnionTest联合的10 : : i=20; std: : cout<<: : i< : endl;//输出全局静态匿名联合的20 i=30; std: : cout< : endl;//输出局部匿名联合的30 return0; } C语言实现C++类 C实现C++的面向对象特性(封装、继承、多态) ∙封装: 使用函数指针把属性与方法封装到结构体中 ∙继承: 结构体嵌套 ∙多态: 父类与子类方法的函数指针不同 explicit(显式)关键字 ∙explicit修饰构造函数时,可以防止隐式转换和复制初始化 ∙explicit修饰转换函数时,可以防止隐式转换,但按语境转换除外 explicit使用 structA { A(int){} operatorbool()const{returntrue;} }; structB { explicitB(int){} explicitoperatorbool()const{returntrue;} }; voiddoA(Aa){} voiddoB(Bb){} intmain() { Aa1 (1);//OK: 直接初始化 Aa2=1;//OK: 复制初始化 Aa3{1};//OK: 直接列表初始化 Aa4={1};//OK: 复制列表初始化 Aa5=(A)1;//OK: 允许static_cast的显式转换 doA (1);//OK: 允许从int到A的隐式转换 if(a1);//OK: 使用转换函数A: : operatorbool()的从A到bool的隐式转换 boola6(a1);//OK: 使用转换函数A: : operatorbool()的从A到bool的隐式转换 boola7=a1;//OK: 使用转换函数A: : operatorbool()的从A到bool的隐式转换 boola8=static_cast static_cast进行直接初始化 Bb1 (1);//OK: 直接初始化 Bb2=1;//错误: 被explicit修饰构造函数的对象不可以复制初始化 Bb3{1};//OK: 直接列表初始化 Bb4={1};//错误: 被explicit修饰构造函数的对象不可以复制列表初始化 Bb5=(B)1;//OK: 允许static_cast的显式转换 doB (1);//错误: 被explicit修饰构造函数的对象不可以从int到B的隐式转换 if(b1);//OK: 被explicit修饰转换函数B: : operatorbool()的对象可以从B到bool的按语境转换 boolb6(b1);//OK: 被explicit修饰转换函数B: : operatorbool()的对象可以从B到bool的按语境转换 boolb7=b1;//错误: 被explicit修饰转换函数B: : operatorbool()的对象不可以隐式转换 boolb8=static_cast static_cast进行直接初始化 return0; } friend友元类和友元函数 ∙能访问私有成员 ∙破坏封装性 ∙友元关系不可传递 ∙友元关系的单向性 ∙友元声明的形式及数量不受限制 using using声明 一条using声明语句一次只引入命名空间的一个成员。 它使得我们可以清楚知道程序中所引用的到底是哪个名字。 如: usingnamespace_name: : name; 构造函数的using声明 在C++11中,派生类能够重用其直接基类定义的构造函数。 classDerived: Base{ public: usingBase: : Base; /*...*/ }; 如上using声明,对于基类的每个构造函数,编译器都生成一个与之对应(形参列表完全相同)的派生类构造函数。 生成如下类型构造函数: Derived(parms): Base(args){} using指示 using指示使得某个特定命名空间中所有名字都可见,这样我们就无需再为它们添加任何前缀限定符了。 如: usingnamespace_namename; 尽量少使用using指示污染命名空间 一般说来,使用using命令比使用using编译命令更安全,这是由于它只导入了指定的名称。 如果该名称与局部名称发生冲突,编译器将发出指示。 using编译命令导入所有的名称,包括可能并不需要的名称。 如果与局部名称发生冲突,则局部名称将覆盖名称空间版本,而编译器并不会发出警告。 另外,名称空间的开放性意味着名称空间的名称可能分散在多个地方,这使得难以准确知道添加了哪些名称。 using使用 尽量少使用using指示 usingnamespacestd; 应该多使用using声明 intx; std: : cin>>x; std: : cout< : endl; 或者 usingstd: : cin; usingstd: : cout; usingstd: : endl; intx; cin>>x; cout< : : 范围解析运算符 分类 1.全局作用域符(: : name): 用于类型名称(类、类成员、成员函数、变量等)前,表示作用域为全局命名空间 2.类作用域符(class: : name): 用于表示指定类型的作用域范围是具体某个类的 3.命名空间作用域符(namespace: : name): 用于表示指定类型的作用域范围是具体某个命名空间的 : : 使用 intcount=11;//全局(: : )的count classA{ public: staticintcount;//类A的count(A: : count) }; intA: : count=21; voidfun() { intcount=31;//初始化局部的count为31 count=32;//设置局部的count的值为32 } intmain(){ : : count=12;//测试1: 设置全局的count的值为12 A: : count=22;//测试2: 设置类A的count为22
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言 C+ 面试 知识 总结