C++经典知识点⾯试题
1、指针的优点和缺点
优点:灵活⾼效
(1)提⾼程序的编译效率和执⾏速度(数组下标往下移时,需要使⽤乘法和加法,⽽指针直接使⽤++即可)
(2)通过指针可使⽤主调函数和被调函数之间共享变量或数据结构,便于实现双向数据通讯。
(3)可以实现动态的存储分配。
(4)便于表⽰各种数据结构,如结构体,编写⾼质量的程序。
缺点:容易出错
(1)可能变成野指针,导致程序崩溃
(2)内存泄露
(3)可读性差
2、指针和引⽤的定义和区别
(1)指针和引⽤的定义
1)指针:指针是⼀个变量,存储⼀个地址,指向内存的⼀个存储单元;
2)引⽤跟原来的变量实质上是同⼀个东西,只不过是原变量的⼀个别名⽽已。
(2)指针和引⽤的区别
<1> 从内存分配上来说:
1)指针是⼀个实体,⽽引⽤仅是个别名,即为指针分配内存,⽽不为引⽤分配内存空间;
<2> 从指向的内容来说:
2)引⽤只能在定义时被初始化⼀次,之后不可变;指针可变;
3)引⽤不能为空,指针可以为空;
4)const与指针搭配可以表⽰指针指向和指针指向内容是否可变。const与引⽤搭配只有⼀种,即来修饰其内容的可读性。由于引⽤从⼀⽽终,不⽤修饰其指向。
5)指针可以有多级,但是引⽤只能是⼀级(int **p;合法,⽽int &&a是不合法的)
<3> 其他⽅⾯
6)"sizeof引⽤"得到的是所指向的变量(对象)的⼤⼩,⽽"sizeof指针"得到的是指针本⾝的⼤⼩;
7)指针和引⽤的⾃增(++)运算意义不⼀样;
指针和引⽤在符号表中的形式:程序在编译时分别将指针和引⽤添加到符号表上。在符号表上记录的是变量名及变量所对应地址。在符号表上,指针变量对应的地址值为指针变量的地址值,⽽引⽤对应的地址值是引⽤对象的地址值。符号表⽣成后就不会再改,因此指针可以改变指向的对象(指针变量中的值可以改),⽽引⽤对象不能改。
3、malloc/free 和 new/delete相关的⾯试题
1):C++有了malloc/free 为什么还要new/delete?
(1)malloc/free只在申请空间时,它们只需要申请空间,⽆法对空间进⾏操作。
(2)⽽在创建C++的对象时,不仅仅是需要申请空间,还需要⾃动调⽤构造函数,以及在对象消亡之前要⾃动执⾏析构函数。
因此 C++语⾔需要⼀个能完成动态内存分配和初始化⼯作的运算符new,以及⼀个能完成清理与释放内存⼯作的运算符delete。
根据上⾯两点,我们可以知道malloc/free 是不能满⾜C++的需要的,因此需要new/delete。
即在创建对象时,能分配内存空间且初始化内存 + 销毁对象时,能回收空间且对内存进⾏清理的new/delete。
2)为什么malloc/free在申请空间时,它们只能申请空间,⽆法对空间进⾏操作?
因为:malloc/free 是库函数,使⽤它需要头⽂件,在⼀定程度上是独⽴于语⾔的。编译器在处理库函数时,编译器不需要知道它是做啥的,⽽仅仅需要对该函数进⾏编译,并且保证调⽤函数时的参数和返回值是合法的,并⽣成相应 call 函数的代码就ok了。编译器不会控制库函数做⼀些操作,⽐如调⽤构造函数和析构函数。因此malloc/free⽆法满⾜动态⽣成对象的要求。
3)为什么new/delete 在申请空间时,它们不仅能申请空间,还能调⽤构造函数或析构函数对对空间进⾏操作?
因为:new/delete是运算符,它与+-*/的地位是⼀样的。编译器看到new/delete时,就知道只要它要做啥操作,并⽣成对应的代码。4):malloc/free 和 new/delete 的相同点和不同点
相同点:它们都可以申请和释放空间。
不同点:
⼀、new/delete 在申请空间的时候能对空间进⾏操作,⽽malloc/free 不能。
(1)new :分配内存 + 调⽤类的构造函数 + 初始化 delete:释放内存 + 调⽤类的析构函数
(2)malloc:只分配内存,不会进⾏初始化类成员的⼯作 free只释放内存,不会调⽤析构函数
⼆、new/delete是C++运算符,能重载
(1)new、delete 是运算符,可以进⾏重载
(2)malloc,free是标准库函数,不可以进⾏重载,但可以覆盖。
三、new delete 更加安全,简单:
(1)不⽤计算类型⼤⼩:⾃动计算要分配存储区的字节数
(2)不⽤强制类型转换:⾃动返回正确的指针类型(⼆者返回值不同,⼀个为void*,⼀个是某种数据类型指针)
四、new可以分配⼀个对象或对象数组的存储空间,malloc不可以
五、new和delete搭配使⽤,malloc和free搭配使⽤:混搭可能出现不可预料的错误
六、new后执⾏的三个操作:(某⾯试题⽬)
(1)new的类分配内存空间。
(2)调⽤类的构造⽅法。
(3)返回该实例(对象)的内存地址
4、构造函数、析构函数与虚函数的关系
(1)为什么构造函数不能是虚函数?
简单点说,构造函数的调⽤是发⽣多态的前提(多态有对象指针引发)。
(2)为什么在派⽣类中的析构函数常常为虚析构函数?
简单点说,发⽣多态时,防⽌漏掉调⽤派⽣类的析构函数。
(3)把所有的类的析构函数都设置为虚函数好吗?
简单点说,不好,会造成时间和空间浪费。
具体答案在博客中。
5、C++中,哪些函数不可以被声明为虚函数
总的来说,共有五种,普通函数(⾮成员函数)、构造函数、内联函数、静态函数、友元函数。
⾸先说明两点:
(1)虚函数是为了实现多态,⽽多态是属于动态联编,在运⾏时确定调⽤哪个函数。
(2)虚函数调⽤时,类之间需要有公有继承 +继承关系 + 基类指针或引⽤调⽤。
具体解释
(1)普通函数为啥不能是虚函数?
原因:多态是依托于类的,要声明的多态的函数前提必须是虚函数。
(2)构造函数为啥不能是虚函数?
原因:多态是依托于类的,多态的使⽤必须是在类创建以后,⽽构造函数是⽤来创建构造函数的,所以不⾏。
具体的原因:虚表指针的初始化时在构造函数进⾏的,⽽虚函数需要放到虚表中。在调⽤虚函数前,必须⾸先知道虚表指针,此时⽭盾就出来了。字符串拷贝函数strcpy作用
(3)内联函数为啥不能是虚函数?
原因:内联函数属于静态联编,即内联函数是在编译期间直接展开,可以减少函数调⽤的花销,即是编译阶段就确定调⽤哪个函数了。但是虚函数是属于动态联编,即是在运⾏时才确定调⽤哪⼀个函数。显然这两个是冲突的。
(4)静态函数为啥不能使虚函数?
原因:
<1>从技术层⾯上说,静态函数的调⽤不需要传递this指针。但是虚函数的调⽤需要this指针,来到虚函数表。相互⽭盾
<2>从存在的意义上说,静态函数的存在时为了让所有类共享。可以在对象产⽣之前执⾏⼀些操作。与虚函数的作⽤不是⼀路的。
(5)友元函数为啥不能是虚函数?
原因:C++不⽀持友元函数的继承,不能继承的函数指定不是虚函数。
6、能做switch()的参数类型是:
能⾃动转换为整形(int)且转换过程中不存在精度损失的类型
具体包括:byte,short,char,int. 但是不包括:long,string,double,float等。
7、堆栈溢出⼀般是由什么原因导致的?
(1)没有回收垃圾资源
(2)递归调⽤的层次太深
8、C++中的空类,默认产⽣哪些类成员函数?
(1)默认的构造函数
(2)默认的拷贝构造函数
(3)默认的赋值函数
(4)默认的析构函数
(5)默认的取地址运算符(不带const)
(6)默认的取地址运算符(带const)
[cpp]
class Empty
{
public:
Empty(); // 缺省构造函数
Empty( const Empty& ); // 拷贝构造函数
~
Empty(); // 析构函数
Empty& operator=( const Empty& ); // 赋值运算符
Empty* operator&(); // 取址运算符
const Empty* operator&() const; // 取址运算符 const
};
9、⾯向对象的四个特性:
(1)抽象性:是对事物的抽象概括描述,继⽽将客观事物抽象成类,从⽽实现了客观世界向计算机世界的转化。
(2)封装性:把客观事物封装成抽象的类,并且只让可信的类活对象进⾏访问,⽽对其他成员进⾏信息隐藏。
(3)继承性:新产⽣的类⽆需重新编写现有类的代码,⽽直接使⽤现有类的功能,继⽽对新类进⾏扩展。
(4)多态性:允许基类指针指向各种各样的派⽣类对象,之后能够根据其指向的派⽣类对象,做出不同的反应(该反应为虚函数实现的)。即多态的出现,就是给出⼀个统⼀处理各种各样的派⽣类的⽅法。⽐如可以定义⼀个数组,数组类型为基类指针类型,放的可以是各种派⽣类,之后可以使⽤基类指针对数组元素统⼀处理。
注:有书上说是四个,有书上说是三个,不是很统⼀。
10、简述strcpy与memcpy的相同点和区别点:
相同点:strcpy与memcpy都可以实现拷贝的功能
不同点:
(1)实现功能不同,strcpy主要实现字符串变量间的拷贝,memcpy主要是内存块间的拷贝。
(2)操作对象不同,strcpy的操作对象是字符串,memcpy 的操作对象是内存地址,并不限于何种数据类型。
(3)执⾏效率不同,memcpy最⾼,strcpy次之。
11、内存分配⽅式
⼀个C、C++程序编译时内存分为5⼤存储区:全局区、栈区、堆区、⽂字常量区、程序代码区。
(1) 在静态存储区域分配
控制者:编译器
分配时间:在程序编译的时候分配内存
释放时间:在程序的整个运⾏期间都存在,程序结束后由OS释放
内容:全局变量,static变量
特点:
0、速度快,不易出错。
1、初始化的全局变量和静态变量在⼀块区域,未初始化的全局变量和静态变量在另⼀块区域
2、定义后,变量的值可以改变
(2) 在栈上创建
控制者:由编译器⾃动分配释放
分配时间:在程序运⾏(执⾏函数)的时候分配内存
释放时间:在函数执⾏结束时,释放存储单元⾃动被释放。
举例:局部变量,函数参数
特点:
1、栈内存分配运算内置于处理器的指令集中,分配效率很⾼,但是分配的内存容量有限。
2、定义后,变量的值可以改变
(3) 从堆上分配
控制者:程序员⼀般由程序员分配和释放,。
分配时间:在程序运⾏(遇见new或malloc)的时候分配内存。
释放时间:程序员⾃⼰决定,若程序员不释放,程序结束时可能由OS回收,但是程序运⾏期间不释放的内存属于内存泄露。
举例:使⽤new 和 malloc申请的空间
特点:
0、频繁地分配和释放不同⼤⼩的堆空间将会产⽣堆内碎块
1、程序员使⽤malloc 或new 申请任意多少的内存,⾃⼰负责在何时⽤free 或delete 释放内存,否则会造成内存泄露。
2、定义后,变量的值可以改变
(4) ⽂字常量区
控制着:编译器
分配时间:在程序编译的时候分配内存
释放时间:程序结束后由系统释放
举例:常量字符串
特点:定义后,变量的值不可以改变,只读的
(5) 程序代码区
内容:存放函数体的⼆进制代码
12.堆内存与栈内存的⽐较
(1)申请⽅式
栈:由系统分配,
堆:有程序员显式分配,malloc和new
(2)申请⼤⼩的限制
栈:在Windows下,栈是向低地址扩展的数据结构,是⼀块连续的内存的区域。栈内存的地址和⼤⼩是系统预先规定好的,⽽且能从栈申请的⼤⼩很⼩,在WINDOWS下,栈的⼤⼩是2M,如果申请的空间超过栈的剩余空间时,将提⽰overflow。
堆:堆是向⾼地址扩展的数据结构,是不连续的内存区域。这是由于系统是⽤链表来存储的空闲内存地址的,⾃然是不连续的,⽽链表的遍历⽅向是由低地址向⾼地址。堆的⼤⼩受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间⽐较灵活,也⽐较⼤。
(3)申请后系统的响应
栈:只要栈的剩余空间⼤于所申请空间,系统将为程序提供内存,否则将报异常提⽰栈溢出。
堆:⾸先应该知道操作系统有⼀个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻第⼀个空间⼤于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。由于到的堆结点的⼤⼩不⼀定正好等于申请的⼤⼩,系统会⾃动的将多余的那部分重新放⼊空闲链表中。
(4)申请效率的⽐较
栈:由系统⾃动分配,速度较快。但程序员是⽆法控制的。
堆:由new分配的内存,⼀般速度⽐较慢,⽽且容易产⽣内存碎⽚,不过⽤起来最⽅便。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论