C++面经之C++

1.C和C++的区别

http://blog.csdn.net/zqixiao_09/article/details/51235444

区别:

C语言是一种结构化语言,面向过程,基于算法和数据结构,所考虑的是如何通过一个过程或者函数从输入得到输出;

C++是面向对象,基于类、对象和继承,所考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题,通过获取对象的状态信息得到输出或实现过程控制。

面向过程

优点:性能比面向对象高,因为类调用需要实例化,开销比较大,比较消耗资源,比如嵌入式开发,单片机开发等一般采用面向过程开发,性能是最重要的因素。

缺点:没有面向对象易维护、易复用、易扩展。

面向对象

优点:易维护、易扩展、易复用,由于面向对象有封装、继承、多态的特性,可以设计出低耦合的系统,是系统更加灵活、更加易于维护。

缺点:性能比面向过程低。

在C++ 程序中调用被 C编译器编译后的函数,为什么要加 extern “C”?

答:C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为: void foo(int x, int y);

该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。
C++提供了C连接交换指定符号extern“C”来解决名字匹配问题。

C语言怎么处理返回值

返回值会被保存在cpu的寄存器中,因此,在返回子函数后,返回值能从寄存器中将返回值赋值给调用函数中的变量。

https://blog.csdn.net/HKaisa/article/details/51785481

C++的类中哪些成员会被算入sizeof中

1.非静态数据成员
2.还有要维护虚函数表的指针,占4字节。

解释:使用sizeof()计算类大小的一些基本原则:

  • 类的大小为类的非静态成员数据的类型大小之和,也就是说静态成员数据不作考虑;
  • 类的总大小也遵守类似class字节对齐的,调整规则;
  • 成员函数都是不会被计算的;
  • 如果是子类,那么父类中的成员也会被计算;
  • 虚函数由于要维护虚函数表,所以要占据一个指针大小,也就是4字节。
    总结:一个类中,虚函数、成员函数(包括静态与非静态)和静态数据成员都不占用类对象的存储空间。

2.C/C++较Java有性能优势,为什么?

https://zhidao.baidu.com/question/559368057.html

3.C++语言特性:构造函数,析构函数,虚函数,内联函数,静态成员函数,重载,覆盖,隐藏

http://blog.csdn.net/iamcxl369/article/details/7773010

Static_cast<>,dynamic_cast<>,const_cast<>,reinterpret_cast<>的各自作用和使用环境?

Static_cast:能完成大部分转换功能,但是并不确保安全

Const_cast:无法从根本上转变类型,如果是const,它就依旧是const,只是如果原对象不是const,可以通过此转换来处理,针对指针和引用而言。

Dynamic_cast:针对基类和派生类指针和引用转换,基类和派生类之间必须要继承关系,是安全的。

Reinterpret_cast:允许将任何指针类型转为其他指针类型,是安全的。



C风格转换是“万能的转换”,但需要程序员把握转换的安全性,编译器无能为力;

static_cast最接近于C风格转换,但在无关类指针转换时,编译器会报错,提升了安全性;

dynamic_cast要求转换类型必须是指针或引用,且在下行转换时要求基类是多态的,如果发现下行转换不安全,dynamic_cast返回一个null指针,dynamic_cast总是认为void*之间的转换是安全的;

reinterpret_cast可以对无关类指针进行转换,甚至可以直接将整型值转成指针,这种转换是底层的,有较强的平台依赖性,可移植性差;

const_cast可以将常量转成非常量,但不会破坏原常量的const属性,只是返回一个去掉const的变量

http://blog.csdn.net/chenlycly/article/details/38713981

4.面向对象的三个特性

http://blog.csdn.net/uestclr/article/details/51553978

C++中可以继承string类吗?为什么?

不可以,因为string不是类

封装,继承,多态。

封装

  • 该公开的就公开,该私有的就隐藏掉,主要是由Public,private实现;作用是便于分工和分模块,防止不必要的扩展。

继承

  • 就是一种传承,可以把父类型的数据传承到子类型中,子类除了传承父类的数据之外,还可以对父类型进行扩展。

    1. 公开继承public
    2. 保护继承protected
    3. 私有继承private
    4. 保护成员:在子类和本类中可以访问,其他不行;
    5. 构造函数和析构函数是不能被继承的,但是可以被调用。并且子类一定会调用父类的构造函数;
    6. 拷贝构造函数和赋值运算符函数也不能被继承:在子类不提供拷贝构造和赋值运算符时,子类默认调用父类的赋值运算符和拷贝构造函数。但子类一旦提供拷贝构造和赋值运算符函数则不再调用父类拷贝构造和赋值运算符函数。

多态

  • 多态:一个父类型的对象的指针或者引用指向或者是引用一个子类对象时,调用父类型中的虚函数,如果子类覆盖掉了虚函数,则调用的是子类覆盖之后的。
  • 继承是构成多态的基础;
  • 虚函数是构成多态的关键;
  • 函数覆盖是构成多态的必备条件;
  • 多态的应用:函数参数,函数返回值。


纯虚函数

纯虚函数的作用:在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行定义。作为接口而存在 纯虚函数不具备函数的功能,一般不能直接被调用。

从基类继承来的纯虚函数,在派生类中仍是虚函数。如果一个类中至少有一个纯虚函数,那么这个类被称为抽象类(abstract class)。

抽象类中不仅包括纯虚函数,也可包括虚函数。抽象类必须用作派生其他类的基类,而不能用于直接创建对象实例。但仍可使用指向抽象类的指针支持运行时多态性。

多态的实现原理

  • 虚函数:成员函数加了vitual修饰符
  • 虚函数表指针:一个类型有虚函数,则对这个类型提供一个指针,这个指针放在生成对象的前四个字节。同类型的对象共享一张虚函数表。并且不同类型的虚函数表地址不同。
  • 虚函数表:虚函数表中的每个元素都是虚函数的地址。

虚函数总结:

  1. 一个类型一旦出现虚函数,则会生成一张虚函数表,虚函数表中存放的就是虚函数的函数地址,通过这个函数地址可以到代码区中去执行对应的函数。
  2. 虚函数表中只存放类型中的虚函数,不是虚函数的一概不管。
  3. 在每个生成的类型对象的前四个字节中存放的是虚函数表指针,通过这个指针可以访问到虚函数表,从而访问其中的函数。
  4. 同种类型共享虚函数表,不同类型有自己独立的虚函数表,继承关系中的子类和父类属于不同类型,所以有自己独立的函数表。

两个用于限制继承和虚函数的关键字C++11新特性的:

override(虚函数重载)和final(阻止继承、虚函数重载)

https://blog.csdn.net/le119126/article/details/50175051

面向对象的五个原则:
  1. 单一功能原则 : 每个类型(包括接口和抽象)功能要求单一,只负责一件事情。
  2. 开放封闭原则:一个软件实体应该对扩展开发,对修改关闭。可扩展但是不可更改。核心:用抽象构建框架,用实现类实现扩展。
  3. 接口隔离原则:使用多个小的专门的接口,而不要使用一个大的总接口。(就是每一个接口都执行一个专门的功能,用什么调什么。这样可以提高代码的灵活性,还可以降低类间的耦合性。提高稳定性。)
  4. 替换原则:子类必须能够替换基类。这一思想体现为对继承机制的约束规范,只有子类能够替换基类时,才能保证运行期内识别子类,这是保证继承复用的基础。
  5. 依赖倒置原则:依赖于抽象,具体来说就是,高层模块不依赖底层模块,二者都通依赖于抽象。抽象不依赖于具体,而具体依赖于抽象。模块间的依赖是通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的;接口或抽象类不依赖于实现类;实现类依赖于接口和抽象类。

4.1.C++ 虚函数表解析

http://blog.csdn.net/haoel/article/details/1948051

4.2.c++虚函数实现机制及内存模型

http://blog.csdn.net/mightysheldor/article/details/49928485

虚函数带来的好处就是: 可以定义一个基类的指针, 其指向一个继承类, 当通过基类的指针去调用函数时, 可以在运行时决定该调用基类的函数还是继承类的函数. 虚函数是实现多态(动态绑定)/接口函数的基础.

通过虚函数表来实现,一个基类的指针指向子类,在进行函数调用的时候通过动态绑定,实现调用的是基类的成员函数或者子类的成员函数。

4.3.虚函数的调用过程?

找到对象内存中vfptr所指向虚函数表的地址-》找到虚函数表相应的虚函数地址

在每个生成的类型对象的前四个字节中存放的是虚函数表指针,通过这个指针可以访问到虚函数表,从而访问其中的函数。

静态函数和静态数据成员http://blog.csdn.net/fanyun_01/article/details/51422357

静态函数能定义为虚函数吗?为什么?
  • 不可以,因为虚函数属于对象,不属于类
静态函数能定义为常函数吗?为什么?
  • 不可以,因为常函数是操作成员变量的,而静态函数没有成员变量可说
  • 类的成员函数后面加 const,表明这个函数不会对这个类对象的数据成员(准确地说是非静态数据成员)作任何改变。
  • 静态函数不包含有编译器提供的隐藏的this指针,它在类没有实例化的时候就存在,所以可以直接用 (类名::函数) 来调用,并且由于没有this指针,所以也就没有特定的成员变量供它用,因为类没有实例化前,这些类成员变量不存在,系统也没有分配空间给这些变量,且没有this指针,也无法调用这些成员变量,所以他只能使用那些类没有实例化前就已经存在的静态变量。
  • 静态成员是可以独立访问的,也就是说,无须创建任何对象实例就可以访问。
静态数据成员定义(初始化)不应该被放到头文件中
  • 因为静态数据成员实际上是类中的全局变量,如果在头文件中初始化,在大多数情况下会引起重复定义这样的错误。(即使加上#ifndef #define #endif或者#pragma once也不行。)
  • 定义(初始化)时不受private和protected访问限制
静态成员函数不可以调用类的非静态成员。因为静态成员函数不含this指针。

4.4.深入理解C++的动态绑定和静态绑定

http://blog.csdn.net/chgaowei/article/details/6427731

需要理解四个名词:

  1. 对象的静态类型:对象在声明时采用的类型。是在编译器确定的。
  2. 对象的动态类型:目前所指对象的类型。是在运行期决定的。对象的动态类型可以更改,但是静态类型无法更改。
  3. 静态绑定:绑定的是对象的静态类型,某特性(比如函数)依赖于对象的静态类型,发生在编译期。
  4. 动态绑定:绑定的是对象的动态类型,某特性(比如函数)依赖于对象的动态类型,发生运行期。

至于那些事动态绑定,那些事静态绑定,有篇文章总结的非常好:
我总结了一句话:只有虚函数才使用的是动态绑定,其他的全部是静态绑定。目前我还没有发现不适用这句话的,如果有错误,希望你可以指出来。

4.5.介绍所有的构造函数,什么情况下要给类写拷贝构造函数

http://blog.csdn.net/tiantang46800/article/details/6938762

构造函数的作用:初始化对象的数据成员。
构造函数的特点:以类名作为函数名,无返回类型。

构造函数的种类:

  • 无参数构造函数(默认构造函数)
  • 一般构造函数(也称重载构造函数),一般构造函数可以有各种参数形式,一个类可以有多个一般构造函数,前提是参数的个数或者类型不同(基于C++的重载)
  • 拷贝构造函数(复制构造函数),复制构造函数参数为类对象本身的引用,用于根据一个已存在的对象复制出一个新的该类的对象,一般在函数中会将已存在对象的数据成员的值复制一份到新创建的对象中。
  • **拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量。

深拷贝和浅拷贝:

  • 如果没有自定义复制构造函数,则系统会创建默认的复制构造函数,但系统创建的默认复制构造函数只会执行“浅拷贝”,即将被拷贝的数据成员的值一一复制给新创建的对象,若该类的数据成员中有指针成员,则会使得新的对象的指针所指向的地址与被拷贝对象所指向的地址相同,delete该指针时则会导致两次重复delete而出错。
  • 实现“深拷贝”,即不让指针指向同一地址,而是重新申请一块内存给新的对象的指针数据成员

浅拷贝
所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。

http://blog.csdn.net/duang0626/article/details/51115364
http://blog.csdn.net/lwbeyond/article/details/6202256

拷贝构造函数:

  • CExample(const CExample& C) 就是我们自定义的拷贝构造函数。可见,拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量。
  • 对于一个类X, 如果一个构造函数的第一个参数是下列之一:
    a) X&
    b) const X&
    c) volatile X&
    d) const volatile X&
    且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数.

防止默认拷贝发生:声明一个私有拷贝构造函数。
甚至不必去定义这个拷贝构造函数,这样因为拷贝构造函数是私有的,如果用户试图按值传递或函数返回该类对象,将得到一个编译错误,从而可以避免按值传递或返回对象。

5.C++的构造函数不可以为虚函数

http://blog.csdn.net/jiadebin890724/article/details/7951520

从存储空间看

  • 虚函数对应一个虚函数表,这个虚函数表其实是存储在对象的内存空间的。那么这时候问题来了,如果构造函数是虚的,就需要通过虚函数表来调用,但是对象还没实例化,也就是还没有内存空间,无法找到虚函数表,所以构造函数不能是虚函数。

从实现上看

  • 虚函数表是在构造函数后才建立的,因而构造函数不可能成为虚函数。

6.析构函数为什么要使用虚函数

主要是考虑到继承,比如假设A是基类,B是继承A的子类,这时定义A类型的指针指向子类B对象,delete A,这时候如果析构函数不是虚函数的话,只会调用基类A的析构函数,不会调用B的析构函数,这样就会造成B的资源没有被释放。

但是如果A的析构函数是使用的虚函数,那么就会调用B的析构函数,一切正常。

内联函数可以是虚函数吗?

当然不能,虚函数意味在运行期确定函数的调用地址,内联函数如同宏的用法一样,相当于在编译期把调用内联的地方加上了函数实现的代码。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值