C++ 知识总结提高问题

1、c++中虚函数是怎样实现的?为什么要有虚函数机制?什么情况会使用多态机制?

通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其内容真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。

作用:主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。   通过基类对象的指针或引用来调用虚函数。

2、列举5种设计模式,并举例说明使用场景

总体来说设计模式分为三大类:

  1. 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
  2. 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
  3. 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

其实还有两类:并发型模式和线程池模式。

面向对象有几个原则:

  1. 单一职责原则 (Single Responsiblity Principle SRP):
  2. 开闭原则(Open Closed Principle,OCP):模块应对扩展开放,而对修改关闭。
  3. 里氏代换原则(Liskov Substitution Principle,LSP):如果调用的是父类的话,那么换成子类也完全可以运行。
  4. 依赖倒转原则(Dependency Inversion Principle,DIP):子类型能够替换掉它们的父类型。
  5. 接口隔离原则(Interface Segregation Principle,ISP):定制服务的例子,每一个接口应该是一种角色,不多不少,不干不该干的事,该干的事都要干
  6. 合成/聚合复用原则(Composite/Aggregate Reuse Principle,CARP):在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用已有功能的目的。
  7. 最小知识原则(Principle of Least Knowledge,PLK,也叫迪米特法则):一个对象应对其他对象有尽可能少的了解

3、string的内存怎么实现的,长度的动态变化怎么处理,为什么两倍扩展?

除了以上四个必须的函数,还实现了一些附加的内容。

若干个运算符重载,这里的几个是常见的运算符,可以加深对String的认识和运算符重载的理解。

两个常用的函数,包括取字符串长度和取C类型的字符串。

两个处理输入输出的运算符重载,为了使用的方便,这里把这两个运算符定义为友元函数。

4、Map底层是怎么实现的?set呢?有什么区别?

①map和set都是关联容器,map以键值对的形式存储,key=value组成pair,是一组映射关系。set只有值,可以认为只有一个数据,并且set中元素不可以重复且自动排序。②map和set支持快速查找和删除,一般使用RB树来实现,当然后面还有用hashtable实现的,使用rb树作为底层结构增删数据都很快,不存在内存移动也就不容易出现迭代器失效的问题,这也就是区别于vector的原因-内存移动。

5、列举你所知道的关联容器并介绍其优缺点?

在实际使用过程中,到底选择这几种容器中的哪一个,应该根据遵循以下原则:

1)如果需要高效的随机存取,不在乎插入和删除的效率,使用vector;

2)如果需要大量的插入和删除元素,不关心随机存取的效率,使用list;

3)如果需要随机存取,并且关心两端数据的插入和删除效率,使用deque;

4)如果打算存储数据字典,并且要求方便地根据key找到value,一对一的情况使用map,一对多的情况使用multimap;

5)如果打算查找一个元素是否存在于某集合中,唯一存在的情况使用set,不唯一存在的情况使用multiset。

6、Pair是什么?

和容器一样,pair也是一种模板类型。一个pair保存两个数据成员,是一个用来生成特定类型的模板,当创建一个pair时,我们必须提供两个类型名,pair的数据成员将具有对应的类型,两个类型不要求一样。它的数据成员是公有的,分别命名为first和second,只需点操作就可以访问其成员。其定义初始化的操作也很简单。

7、空类中有哪些成员?为什么要有?

六大默认成员函数分别是:

①构造函数②拷贝构造函数③析构函数④赋值运算符重载⑤取地址操作符重载⑥被const修饰的取地址操作符重载

8、static成员变量如何存储的?static成员函数和普通函数有什么区别?

静态数据成员要在程序一开始运行时就必须存在。因为函数在程序运行中被调用,所以静态数据成员不能在任何函数内分配空间和初始化。

区别在于:

类的静态成员属于类本身,在类加载的时候就会分配内存,可以通过类名直接去访问;非静态成员属于类的对象,所以只有在类的对象产生时才会分配内存,然后通过类的对象去访问。

非静态的函数由类对象(加.或指针加->;)调用,这时将向函数传递this指针.而静态函数由类名(::)(或对象名.)调用,但静态函数不传递this指针,不识别对象个体,所以通常用来对类的静态数据成员操作.

9、介绍c++的类型转换,为什么要使用?

①static_cast:可以实现C++中内置基本数据类型之间的相互转换,enum、struct、 int、char、float等。它不能进行无关类型(如非基类和子类)指针之间的转换。

②const_cast:操作不能在不同的种类间转换。相反,它仅仅把一个它作用的表达式转换成常量。它可以使一个本来不是const类型的数据转换成const类型的,或者把const属性去掉。

③reinterpret_cast: 有着和C风格的强制转换同样的能力。它可以转化任何内置的数据类型为其他任何的数据类型,也可以转化任何指针类型为其他的类型。它甚至可以转化内置的数据类型为指针,无须考虑类型安全或者常量的情形。不到万不得已绝对不用。

④dynamic_cast:

(1)其他三种都是编译时完成的,dynamic_cast是运行时处理的,运行时要进行类型检查。

(2)不能用于内置的基本数据类型的强制转换。

(3)dynamic_cast转换如果成功的话返回的是指向类的指针或引用,转换失败的话则会返回NULL。

(4)使用dynamic_cast进行转换的,基类中一定要有虚函数,否则编译不通过。需要检测有虚函数的原因:类中存在虚函数,就说明它有想要让基类指针或引用指向派生类对象的情况,此时转换才有意义。 这是由于运行时需要检查类型信息,而这个信息存储在类的虚函数表(关于虚函数表的概念,详细可见<Inside c++ object model>)中,只有定义了虚函数的类才有虚函数表。

(5) 在类的转换时,在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的。在进行下行转换 时,dynamic_cast具有类型检查的功能,比static_cast更安全。向上转换即为指向子类对象的向下转换,即将父类指针转化子类指针。向下转换的成功与否还与将要转换的类型有关,即要转换的指针指向的对象的实际类型与转换以后的对象类型一定要相同,否则转换失败。

10、构造函数是否可以为虚函数、析构函数是否可以为虚函数、静态成员函数是否可以为虚函数?为什么?

(1)当存在类继承并且析构函数中有必须要进行的操作时(如需要释放某些资源,或执行特定的函数)析构函数需要是虚函数,否则若使用父类指针指向子类对象,在delete时只会调用父类的析构函数,而不能调用子类的析构函数,从而造成内存泄露或达不到预期结果;

(2)虚函数能不能是内联函数要取决于具体情况:虚函数是通过基类的对象调用的就可以内联,如果虚函数是通过基类指针指向的子类对象,这个时候是要多态调用,不能在编译期间确定内联,所以是不能内联的。其实inline都只是一个申请,最终由编译器决定内联还是不内联。如下:b是基类对象,可以内联;ptr是基类指针,但是指向的是子类对象,这个时候不能内联。

(3)构造函数不能为虚函数:构造函数在进行调用时还不存在父类和子类的概念,父类只会调用父类的构造函数,子类调用子类的,因此不存在动态绑定的概念;但是构造函数中可以调用虚函数,不过并没有动态效果,只会调用本类中的对应函数;

(4)静态成员函数不能为虚函数:静态成员函数是以类为单位的函数,与具体对象无关,虚函数是与对象动态绑定的。

  • 虚函数可以是内联函数,内联是可以修饰虚函数的,但是当虚函数表现多态性的时候不能内联。
  • 内联是在编译期建议编译器内联,而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因此虚函数表现为多态性时(运行期)不可以内联。
  • inline virtual 唯一可以内联的时候是:编译器知道所调用的对象是哪个类(如 Base::who()),这只有在编译器具有实际对象而不是对象的指针或引用时才会发生

11、画图说明c++虚函数的实现机制、虚表的编译器实现、虚函数的开销

 

12、STL的设计是面向函数还是面向对象以及为什么这样设计?

面向函数,STL的设计目是复用性的提升,复用性必须建立在某种标准之上-不论是语言层次的标准,或数据交换的标准,或通讯协议的标准。可以让你重复运用既有的算法,而不必在环境类似的情况下一再重新撰写相同代码。STL算法是泛型的,不与任何特定数据结构或对象型别束缚在一起,但是却像为你量身定做一样,有很高的效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值