前言
总结了C++常见的问答题,记录下来以便自己回顾,如果能顺便帮到正在备考的小伙伴那就更好了;
数组、指针
什么是数组?
数组在定义前需要确定数组规模;
定义数组会分配一片连续的空间;
这片连续的空间存放的必须是相同的元素;
支持随机读取,但是插入和删除时需要移动大量的元素;
什么是多维数组?
数组元素本身又是一个数组的数组称为多维数组;
传递一个数组为什么需要两个参数?
因为数组传递本质上只传递了数组的起始地址,数组中元素个数需要另一个参数来指出;
如何传递字符串?为什么传递字符串只需要一个参数?
字符串存储在一个字符类型的数组中,所以传递一个字符串也是传递一个数组;
C++规定每个字符串必须以’\0’结束,所以传递字符串时就不需要指出元素个数了;
指针与引用区别
指针是一个变量,里面存的是其他变量的地址;引用是已知变量的别名;
初始化:引用必须在创建时被初始化;指针可以在任何时间被初始化;
是否可变:引用只能在定义时被初始化一次(从一而终),之后不可变;指针可变;
是否可为空:引用不能为空,引用必须连接到一块合法的内存;指针可以为空。
函数
什么是形式参数,实际参数?
形式参数指出函数调用时应该给出的参数个数和类型;
实际参数是函数某次调用时真正的输入数据,是形式参数的初值;
什么是值传递?
在值传递中,形参有自己的存储空间,实参是形参的初值,参数传递完成后实参和形参就再无关系,形参也随函数执行结束而消亡;
C++如何实现函数重载的
编译器为每个函数重新取一个内部名字;发生重载函数调用时,编译器分析实际参数的个数和类型,确定一个形式参数表与实际参数表一致的重载函数,将这个重载函数的内部名字替代函数调用时的函数名;
静态的局部变量和普通的局部变量有何不同?
普通的局部变量随函数执行而生成,函数执行结束而消亡;静态的局部变量在函数的第一次执行时生成,直到程序执行结束而消亡;当再次执行该函数时,其他的局部变量又重新生成了,而静态局部变量不会重新生成;需要用到静态局部变量时,会到第一次函数执行时为其分配的空间进行操作;通过这种方式,函数上一次执行的某些信息可以用在下一次执行中;
类
面向对象的三大特性
封装:将数据(属性)和对数据的操作(函数)包围起来,抽象成一个对象,能够隐藏实现细节,减少外界的干扰和误用,从而更加模块化以及安全;
继承:在基类的基础上,扩充属性和函数而得到一个更强大的类;
多态:(一般指运行时多态性)由基类指针指向不同的派送类对象,执行函数名相同但功能不同的函数;
多态
多态分为两种,一种是编译时多态性,另一种是运行时多态性;
编译时多态性指的是编译器根据相同的指令调用不同的函数,可通过重载函数或运算符重载来实现;
运行时多态性指的是运行时根据基类指针指向不同的派生类对象,调用不同的函数,可通过虚函数实现;
虚函数和纯虚函数
虚函数指的是基类中用virtual修饰的函数,引入虚函数使得派生类能够根据自己的需求重新定义继承的函数;(普通函数也可以重写,但是基类无法访问派送类重写的普通函数);虚函数有函数体,可以调用,也可以作为多态的一种手段(运行时多态);
纯虚函数没有函数体,无法调用;
struct和union的区别
最大区别在于内存利用
结构体struct:各成员各自拥有自己的内存,各自使用互不干涉,同时存在,遵循内存对齐原则。一个struct变量的总长度等于所有成员的长度之和。
联合体union:各成员共用一块内存空间,并且同时只有一个成员可以得到这块内存的使用权(对该内存的读写),各变量共用一个内存首地址。因而,联合体比结构体更节约内存。一个union变量的总长度至少能容纳最大的成员变量,而且要满足是所有成员变量类型大小的整数倍。不允许对联合体变量名直接赋值或其他操作。
深拷贝和浅拷贝的区别
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
如果拷贝构造函数中有指针,那么要自己实现深拷贝;
什么情况下系统会自动调用复制构造函数(拷贝构造函数)?
- 用一个对象去初始化同一类的另一个新对象时;
- 函数的形参是类的对象,调用函数进行形参和实参相结合时;
- 函数的返回值是类的对象,函数执行返回调用时;
什么是组合?什么是继承?
组合是将某个类作为当前类的成员对象,反映的是has-a的关系;
继承是在已有类的基础上,再增加一些属性和方法来得到一个功能更强大的类,反映的是is-a的关系;
抽象类
包含有纯虚函数的类是抽象类,引入抽象类的目的是为了规范派生类的行为,不可以定义抽象类对象,可以定义抽象类的指针;
为什么要定义虚析构函数
在销毁基类指向的派生类对象时,如果不采用虚析构函数,则只能调用基类的析构函数而不能调用派生类的析构函数导致无法回收派生类所申请的内存空间,造成内存泄漏;
基类指针可以指向派生类的对象,为什么派生类的指针不能指向基类对象
派生类对象中包含了一个基类对象,当基类指针指向派生类对象时,通过基类指针可以访问派送类中的基类部分。但如果用一个派生类指针指向基类对象,通过派生类指针访问派生类新增加的成员,将无法找到这些成员;
说明派生类对象的构造和析构次序
先执行基类的构造函数,再执行派生类的构造函数;先执行派生类的析构函数,再执行派生类的析构函数;
说明拥有对象成员的派送类的构造过程
先执行基类的构造函数,再执行对象成员的构造函数,最后执行派生类的构造函数;
虚基类
虚基类是一种派生方式,用来解决多重多级继承造成的二义性问题,保证派送类只有一个基类对象;虚基类的构造函数的参数必须由最新派生出来的类负责初始化,虚基类的构造函数先于非虚基类的构造函数执行;
运算符重载
如何区分++的前缀用法和后缀用法的重载函数?
后缀++的重载运算符函数中增加了一个整形的参数;该参数只是为了区分这两个函数,其值无任何意义;
返回结果时,前缀++返回运算后的结果,后缀++返回运算前的结果;
如果类的设计者在定义一个类时没有定义任何成员函数,那么这个类有几个成员函数?
- 默认的构造函数;
- 默认的析构函数;
- 复制构造函数;
- 赋值运算符重载函数;
复制构造函数和赋值运算符重载函数的区别?
复制构造函数和赋值运算符重载函数使用场景不同:
复制构造函数用于创建一个对象时,用另一个已存在的同类对象对其进行初始化;
对于已存在的两个对象,可以通过赋值运算符用一个对象的值来改变另一个对象的值;
其他
面向对象和面向过程的区别
面向过程:将问题分解一个个步骤,用函数实现这些步骤,依次调用函数来解决问题;
面向对象:将问题分解为一个个对象,通过对象的属性和行为来解决问题;
栈堆
Stack的空间由操作系统自动分配/释放,Heap的空间手动分配/释放;
Stack空间有限,Heap是很大的自由存储区;
C中的malloc函数分配的内存空间即在堆上,C++中对应的是new操作符。
程序在编译期对变量和函数分配内存都在栈上进行,且程序运行过程中函数调用时参数的传递也在栈上进行。
参考
《C++程序设计(慕课版)》翁惠玉 俞勇