c++使用单向链表存储一组有序数据_C++面试题

本文详细介绍了C++中使用单向链表存储一组有序数据的相关知识点,包括链表的特性、动态内存分配、构造与析构函数、多态、引用、虚函数、抽象类、常量引用、内存管理等。此外,文章还提供了C++面试中常见的链表、内存、多态、模板、STL等题目解析,帮助读者深入理解C++编程中的关键概念和技术。
摘要由CSDN通过智能技术生成

更新至【58】,不定期更新,加入了自己的理解,如有不对,请指出。

【1】 new/delete和malloc/free的区别和联系?

1. 二者都可以动态分配和撤销内存。

2. new/delete是运算符,执行效率更高,而后者是标准函数库。

3. 针对对象时,new/delete会执行对象的构造/析构函数,而后者不会。

4. new返回数据类型指针,malloc返回void指针。

【2】 delete和delete [ ]的区别?

后者是delete处理数组时的用法,它会调用每个数组元素的析构函数,然后释放内容,例如:pt = new class[10]; delete [ ]pt;简单说就是:delete与new配套,delete [ ]与new [ ]配套。

【3】C++的特性?

封装、继承、多态。

【4】子类析构时会调用父类的析构函数吗?

1. 会,但是调用的次序是先派生类的析构,后基类的析构,也就是说在基类的的析构调用的时候,派生类的信息已经全部销毁了;

例如运行以下代码,输出:

constrcut parent

constrcut child

destrcut child

destrcut parent

1 classParent2 {3 public:4 Parent()5 {6 std::cout << "constrcut parent" <<:endl std::cout parent class child : publicparent14 public:16>

26 intmain()27 {28 Child child;29 return 0;30 }

2. 而构造函数的情况则相反:定义一个对象时先调用基类的构造函数、然后调用派生类的构造函数,例如:

Graduate(int age, string name, double wage):Student(age, name){mwage = wage;}

|__________|_________________________↑        ↑

|______________________________|

3. 当父类构造函数无参数时,子类会自动调用父类的无参数构造函数,如1所示,输出

constrcut parent

constrcut child

destrcut child

destrcut parent

【5】 C++多态靠什么实现的?

广义上的多态包括静态/动态多态。

1. 其中前者靠函数/运算符重载实现,体现在程序编译的时候,因为通过单行语句就能判断调用那个函数(根据参数类型和个数等),所以程序在编译的时候就能确定调用那个函数;

2. 而动态多态靠虚函数实现,体现在程序运行的时候,因为只凭一行程序无法确定调用那个函数,例如pt->display();必须结合“上下文”,所以在程序运行的时候才能确定调用那个函数。

【6】 虚函数的作用

虚函数:在基类中用virtual标记的成员函数例如virtual void display();允许在派生类中重写(override),函数类型,名,参数必须一样,最好也加上virtual关键字,只有函数体不一样,而且重写的函数依旧被自动设为虚函数(不管你加不加virtual,但是最好加上)。

然后重点是,敲黑板:可以通过基类指针调用基类/派生类的同名函数,而不出错,意思是指针类型不用变,直接改变指向就行,例如下,如果不是虚函数,那么下例两次display都是调用的父类的display:

1 Graduate a;2 Student *pt = newStudent ();3 pt -> display();//输出父类的display

4 pt = &a;5 pt -> display();//输出子类的display

通过虚函数与定义一个指向基类对象类型的指针配合使用,就能方便的调用同一个类族中的不同class的同名函数,只需要改变指针的指向即可。那么问题来了,费这么大劲干嘛,上例中直接a.display();不就得了嘛?a.display();编译就知道调用那个函数了,但是pt->display(), 在编译的时候不知道,必须运行的时候“结合上下文”才能知道调用那个函数,这就是多态吧。

【7】 纯虚函数与虚函数的区别是,纯虚函数的作用?

1. 纯虚函数没有函数体,一般如virtual void display() =0;

2. 拥有纯虚函数的类成为抽象类,抽象类不能定义对象(即不能实例,比如动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。

3. 纯虚函数的作用主要是为一个类族提供一个公共接口。

【8】 引用和指针的区别?

1. 引用是一个变量的别名,而指针一个变量,存的是另一个变量的地址。

2. 引用不占用内存,声明一个引用也不是新定义了一个变量。

3. 引用在定义的时候必须初始化,而且以后不能赋值改变用作其他变量的别名,如下,而指针不是必须得初始化,随时赋值。

1 string str1 = "a";2 string str3 = "b";3 string &str2 =str1;4 cout << "a =" << str1 <str1 = str3;但是不能试图成为其他变量的别名,如: &str2 = str3;

6 cout << "a =" << str1 <

4. 指针可以为NULL,但是引用不能为空。

5. 不能建立数组的引用,意思是数组的元素不能是引用,而数组名是可以被引用的,如下:

1 int a[3] ={1111,999,88};2 int (&b)[3] =a;3 std::cout << "a[2] =" << b[2] << std::endl;//输出88,没什么问题4 //还是auto大法好,自动型别推导

5 auto &c =a;6 std::cout << "a[2] =" << c[2] << std::endl;//输出88,没什么问题

这样才是建立一个“数组元素是引用”的数组:int& b[3];但是C++不支持,编译失败。

*6. "sizeof引用"得到的是所指向的变量(对象)的大小,而"sizeof指针"得到的是指针本身的大小。

*7. 指针和引用的自增(++)运算意义不一样。

*8. 指针可以有多级,但是引用只能是一级(int **p;合法 而 int &&a是不合法的)  (右值引用)

【9】 将引用作为函数参数有什么特点?

1. 可以用作输出参数,实现多变量的输出,普通函数只能return一个变量,太不方便了。

2. 相比使用指针达到上述效果,被调函数不要给指针形参(普通形参也是一样的)分配存储空间(深有体会,当形参是很大的一个数据结构时,使用引用参数,相当好,晕死,人家相比的是指针,又不是值传递),效率高,使用方便。

【10】 什么时候使用常引用?

如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。常引用声明方式:const 类型标识符 &引用名=目标变量名;

//例1

inta ;const int &ra = a;

ra= 1; //错误

a = 1; //正确

【11】 将“引用”作为函数返回类型的格式,好处,要遵守的规则?

1. 格式:类型标识符 & 函数名(参数){函数体}

2. 好处

(1) 在内存中不产生被返回值的副本。

(2) 可将函数作为左值运算,例如:

int vals[10];int &put(intn) {returnvals[n];}intmain(int argc, char** argv) {

put(0)=10; //以put(0)函数值作为左值,等价于vals[0]=10;

put(9)=20; //以put(9)函数值作为左值,等价于vals[9]=20;

cout << vals[0] <

cout<< vals[9] <

return 0;

}

3. 注意事项:

(1) 不能返回函数内部局部变量的引用,因为离开函数体后,局部变量的生命周期结束,相应的引用也会失效。

(2) 不能返回函数内部new分配的内存的引用,因为当被函数返回的引用只是作为一个临时变量没有赋值一个实际变量,则new的内存就不会被释放,例如:

//下面是伪代码,没有将new的内存赋值给一个实际变量

cloud_ptr function()

{return (new pcl::PointCloud<:pointxyzi>);

}

std::cout<< function()->points[0].x << std::endl;

【12】 struct和union的区别?

1. 二者都是由多个不同的数据类型成员组成, 但在任何同一时刻, union中只存放了一个被选中的成员, 而struct的所有成员都存在。

2. struct各成员都有自己的内存空间,struct的size等于所有成员的size之和;而union所有成员共用一个内存空间,其size等于长度最长的成员size。

3. 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。

【13】 重载,重写(覆盖),隐藏的区别?(overload,override,overwrite)

1. 重载是同层次(可以理解为“横向”)下的函数同名问题,但是不同参(类型,数量); 在编译的时候就确定调用那个函数,因此又被理解为“静态”多态。

2. 重写是基类与派生类(可以理解为“纵向”)下的函数同名问题,同名同参同类型,只有函数体不一样,基类中的同名函数必须加virtual关键字,派生类可加可不加(一般加上);在程序运行的时候才能确定调用哪个函数,因此又被理解为“动态”多态。

3. 隐藏也是“纵向”的问题,指的是派生类的函数屏蔽了与其同名的基类函数,规则如下:(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。

4. 总结:“纵向”问题中,只有派生类与基类的同名、同参、同类型、并且基类有virtual关键字的时候才是重写,其他的“纵向”问题都是隐藏;而重载出现在“横向”问题中,例如:下面的程序中:

(1) 函数Derived::f(float)  重写了Base::f(float)。(2) 函数Derived::g(int)    隐藏了Base::g(float),而不是重载。(3) 函数Derived::h(float) 隐藏了Base::h(float),而不是重写。

#include

classBase {public:virtual void f(float x) {cout <

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值