C++高频面试问题

C++常见面试问题汇总

一、指针和引用的区别

1.指针有自己的一块空间,而引用只是一个别名;
2.使用sizeof看一个指针的大小是4,而引用则是被引用对象的大小;
3.指针可以被初始化为NULL,而引用必须被初始化且必须是一个已有对象 的引用;
4.作为参数传递时,指针需要被解引用才可以对对象进行操作,而直接对引 用的修改都会改变引用所指向的对象;
5.可以有const指针,但是没有const引用;
6.指针在使用中可以指向其它对象,但是引用只能是一个对象的引用,不能 被改变;
7.指针可以有多级指针(**p),而引用只有一级;
8.指针和引用使用++运算符的意义不一样;
(指针做自增运算是指向后面的内存,引用相当于一个别名,引用做自增运算就是对原对象做自增运算)
9.如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄露。

二、堆和栈的区别

1.堆栈空间分配的区别
栈:由操作系统自动分配和释放,存放函数的参数值,局部变量的值等,其操作方法类似于数据结构中的栈。

堆:一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收,分配方式类似于链表

2.堆栈缓存方式的区别
栈使用的是一级缓存,他们通常都是被调用时处于存储空间中,调用完毕立即释放。

堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定,所以调用这些对象的速度要低一些

3.堆栈数据结构的区别
栈是一种先进后出的数据结构
堆可以被看作一棵树

三、new和delete是如何实现的,new 与 malloc的异同处

new/new[]和delete/delete[]是操作符;是C++用来实现动态内存管理的操作符;

new/new[]操作符是用来申请空间的;
delete/delete[]操作符是用来释放动态申请出来的空间

—new和delete的实现原理
new在底层调用operator new全局函数来申请空间;
delete在底层通过operator delete全局函数来释放空间;

/*该函数实际通过malloc来申请空间,申请成功时直接返回,申请空间失败,尝试
执行空 间不足应对措施,如果改应对措施用户设置了,则继续申请,否则抛异常。*/

void *_CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc){
	void *p;
	while((p=malloc(size))==0)
	if(_callnewh(size)==0){
		static const std::bad_alloc nomem;
		——RAISE(nomen);
}
	return (p);
}
void oprator delete(void *pUserData){
	_CrtMemBlockHeader *pHead;

	RTCCALLBACK(_RTC_Free_hook,(pUserData,0));

	if(pUserData==NULL)
		return ;
	
	_mlock(_HEAP_LOCK);
	_TRY
		phead =pHdr(pUserData);

		_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
		
		_free_dbg( pUserData, pHead->nBlockUse );
	
__FINALLY
		_munlock(_HEAP_LOCK); 
__END_TRY_FINALLY
	
	 return;

}

#define free(p) _free_dbg(p, _NORMAL_BLOCK)

通过这两个全局函数的实现,知道了operator new 实际是通过malloc来申请空间的,operator delete实际是通过free来释放空间的;

—new和malloc的区别
1.malloc与free是C语言的标准函数,new/delete是C++的运算符

2.他们都可用于申请动态内存和释放内存,new和delete在对象创建的时候自动执行构造函数,对象消亡之前会自动执行析构函数

3.new返回指定类型的指针,并且可以自动计算所需的大小
malloc必须用户指定大小,并且默然返回类型为void*,必须强行转换为实际类型的指针。

四、C和C++的区别

1.C是面向过程的,C++是面向对象的
2.C中的const和C++中的const的用法不同。

-与C语言不同,C++const不是只读变量,c语言中const实质上是只读变量
-C++中的const是一个真正意义上的常量
-C++编译器可能会为const分配空间
-c语言中的const变量是可以通过指针修改,而C++不行

3.C中的static和C++中的static的用法不同。
4.C中函数不能进行重载,C++中进行函数重载。
5.C中不能设置函数默认值,C++中可以设置。(返回值也不同,c中没有返回值类型默认为int,C++中则会报错)
6.C++中有内联函数
7.C中动态申请内存用的是malloc/free,C++中用的是new/delete。
8.C中函数传参的方式有传指针和传值,C++中多了个传引用(底层还是传指针)。
9.C中作用域是全局和局部,C++中多了个命名空间和类内作用域。

五、Struct和class的区别

1.默认的继承访问权不同。class默认的是private,struct默认的是public

2.默认访问权限。struct作为数据结构的实现体,它默认的数据访问控制是public,而class作为对象的实现体,它默认的成员变量访问控制是private的

3.class这个关键字还用于定义模板参数,就像“typename”,但关键字“struct”不用于定义模板参数

从上面的区别,我们可以看出,struct更适合看成是一个数据结构的实现体,class更适合看成是一个对象的实现体。

六、define 和const的区别

1.就起作用的阶段而言:#define是在编译的预处理阶段起作用,而const是在编译、运行的时候起作用

2.就起作用的方式而言:#define只是简单的字符串替换,没有类型检查,而const有对应的数据类型,是要进行判断的,可以避免一些低级的错误

3.就存储方面而言:#define只是进行展开,有多少地方使用,就替换多少次,它定义的宏常量在内存中有若干个备份,const定义的只读变量在程序运行过程中只有一份备份

4.从代码调试的方便程度而言:const常量可以进行调试,define是不能进行调试的,因为在预编译阶段就已经替换掉了

七、在C++中const和static的用法

1.static的作用
**-可以隐藏:**全局静态变量只能在本文件内使用
**-默认初始化:**默认初始化为0

变量
全局变量:修饰的全局变量,指定其内部链接,也就是只能本文件使用。

局部变量:修饰的局部变量,改变其生命周期,并不会修改器作用域。

成员变量:只属于类,不属于对象。使用的适合可以通过类名或者对象引用。修饰的成员变量必须在类外单独初始化,如果同时被const 修饰则可以在定义的适合进行初始化。

函数
普通函数:修饰的普通函数,指定其内部链接,也就是只能本文可见。

类成员函数:静态成员函数只属于类,不属于对象。没有this指针,所以它不能访问非静态成员函数 ,和非静态成员变量。 它是用来处理静态成员数据,如果我们非要使用静态成员函数访问非静态成员函数或者非静态成员变量,我们可以间接使用类进行引用。

2.const的作用
修饰变量:c语言中const将一个变量转化为常变量,存储在静态文本段,只有读取权限,C++中同样会将一个变量转化成常量,C++会对其进行优化,将其放入寄存器中,如果想去内存中读取该数据时,我们可以使用volatile关键字进行修饰,保证其可见性。

修饰指针变量:如果const位于* 左侧时,不能修改指针所指的对象(但是可以改变内容),如果const位于* 右侧时,不能修改指针的指向,所以必须初始化。

修饰参数:作用是原参数在该函数中不可被改变。

修饰的返回值:是用const来修饰返回的指针或引用,保护指针指向的内容或引用的内容不被修改,也常用于运算符重载。归根究底就是使得函数调用表达式不能作为左值。

修饰成员变量:该变量只能在初始化列表里初始化。

修饰成员函数:在成员函数后面加上const,const修饰this指针所指的的对象,也就是保证调用该成员函数的对象,在成员函数内部不会改变。(改变权限,权限可缩小,但是不可扩大)

C++的顶层const和底层const

1.指向常量的指针:代表不能改变其指向内容的指针,声明时const可以放在类型名前后都可,声明时:const int和int const是等价的,声明指向常量的指针也就是底层const

int num_a = 1;
int const  *p_a = &num_a; //底层const
//*p_a = 2;  //错误,指向“常量”的指针不能改变所指的对象

注意:指向“常量”的指针不代表它所指向的内容一定是常量,只是代表不能通过解引用符(操作符*)来改变它所指向的内容。上例中指针p_a指向的内容就不是常量,可以通过赋值语句:num_a=2; 来改变它所指向的内容。

2.指针常量:代表指针本身是常量,声明时必须初始化,之后它存储的地址值就不能再改变,const必须放在指针符号后面,即const,声明常量指针就是顶层const

int num_b = 2;
int *const p_b = &num_b; //顶层const
//p_b = &num_a;  //错误,常量指针不能改变存储的地址值

区分顶层const和底层const的作用
1.执行对象拷贝时有限制,常量的底层const不能赋值给非常量的底层const

int num_c = 3;
const int *p_c = &num_c;  //p_c为底层const的指针
//int *p_d = p_c;  //错误,不能将底层const指针赋值给非底层const指针
const int *p_d = p_c; //正确,可以将底层const指针复制给底层const指针

2.使用强制类型转换函数const_cast时,需要能够分辨底层const和顶层const,因为const_cast只能改变运算对象的底层const

int num_e = 4;
const int *p_e = &num_e;
//*p_e = 5;  //错误,不能改变底层const指针指向的内容
int *p_f = const_cast<int *>(p_e);  //正确,const_cast可以改变运算对象的底层const。但是使用时一定要知道num_e不是const的类型。
*p_f = 5;  //正确,非顶层const指针可以改变指向的内容
cout << num_e;  //输出5
发布了26 篇原创文章 · 获赞 9 · 访问量 1万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章