总结面试

1、如何判断一个浮点数是否等于0?

这个问题涉及到计算机的存储方式,对于浮点数来讲计算机表示只能使用精度来无限接近,所以判断浮点数是否为0,只是比较它是否在最接近0的区间内,在就为0;

#include <iostream>

#define DBL_EPSILON      2.2204460492503131E-16

using namespace std;

void isZero(double d)
{
	if (d >= -DBL_EPSILON && d <= DBL_EPSILON)
	{
		cout << "YES";
	}
	else
	{
		cout << "NO";
	}
}

int main()
{
	isZero(0.001);
	getchar();
	return 0;
}

2、tcp的状态

3、动态多态,静态多态

静态多态就是在系统编译期间就可以确定程序执行到这里将要执行哪个函数,例如:函数的重载,对象名加点操作符执行成员函数等,都是静态多态,其中,重载是在形成符号表的时候,对函数名做了区分,从而确定了程序执行到这里将要执行哪个函数,对象名加点操作符执行成员函数是通过this指针来调用的。

动态多态则是利用虚函数实现了运行时的多态,也就是说在系统编译的时候并不知道程序将要调用哪一个函数,只有在运行到这里的时候才能确定接下来会跳转到哪一个函数的栈帧。虚函数就是在基类中声明该函数是虚拟的(在函数之前加virtual关键字),然后在子类中正式的定义(子类中的该函数的函数名,返回值,函数参数个数,参数类型,全都与基类的所声明的虚函数相同,此时才能称为重写,才符合虚函数,否则就是函数的重载),再定义一个指向基类对象的指针,然后使该指针指向由该基类派生的子类对象,再然后用这个指针来调用改虚函数,就能实现动态多态。

4、vector和list的区别

vector为存储的对象分配一块连续的地址空间,因此对vector中的元素随机访问效率很高。在vecotor中插入或者删除某个元素,需要将现有元素进行复制,移动。如果vector中存储的对象很大,或者构造函数复杂,则在对现有元素进行拷贝时开销较大,因为拷贝对象要调用拷贝构造函数。对于简单的小对象,vector的效率优于list。vector在每次扩张容量的时候,将容量扩展2倍,这样对于小对象来说,效率是很高的。

list中的对象是离散存储的,随机访问某个元素需要遍历list。在list中插入元素,尤其是在首尾插入元素,效率很高,只需要改变元素的指针。删除vector里的数据不会释放内存,使用clear()也不会释放内存,

当vector离开他的生存期的时候,它的析构函数会把vector中的元素销毁,并释放它们所占的空间。不过如果vector存放的是指针,那么当vector销毁的时候,那些指针指向的对象并不会销毁,那些内存不会被释放。

5、怎么防止类的实例化

(1)将类定义为抽象基类或者将构造函数声明为private; 
(2)不允许类外部创建类对象,只能在类内部创建对象

6、实例化子类先调用父类的构造函数还是子类的,析构函数呢?

构造原则如下:

    1. 如果子类没有定义构造方法,则调用父类的无参数的构造方法。

    2. 如果子类定义了构造方法,不论是无参数还是带参数,在创建子类的对象的时候,首先执行父类无参数的构造方法,然后执行自己的构造方法。

    3. 在创建子类对象时候,如果子类的构造函数没有显示调用父类的构造函数,则会调用父类的默认无参构造函数。

    4. 在创建子类对象时候,如果子类的构造函数没有显示调用父类的构造函数且父类自己提供了无参构造函数,则会调用父类自己的无参构造函数。

    5. 在创建子类对象时候,如果子类的构造函数没有显示调用父类的构造函数且父类只定义了自己的有参构造函数,则会出错(如果父类只有有参数的构造方法,则子类必须显示调用此带参构造方法)。

    6. 如果子类调用父类带参数的构造方法,需要用初始化父类成员对象的方式,比如:

 
 

#include <iostream.h>   class animal   {   public:     animal(int height, int weight)     {       cout<<"animal construct"<<endl;     }        };   class fish:public animal   {   public:     fish():animal(400,300)     {       cout<<"fish construct"<<endl;     }        };   void main()   {     fish fh;   }

  在fish类的构造函数后,加一个冒号(:),然后加上父类的带参数的构造函数。这样,在子类的构造函数被调用时,系统就会去调用父类的带参数的构造函数去构造对象。这种初始化方式,还常用来对类中的常量(const)成员进行初始化,如下面的代码所示:  

 
 

class point   {   public:      point():x(0),y(0)   private:      const int x;      const int y;   };

析构函数先调用子类,再调用父类。

7、程序崩溃怎么调试

根据问题定位程序可能出错的地方,审查代码,对于指针,内存检查是否出现问题?

根据返回值判断问题的大致类型,定位问题出现的地方,打印参数,利用调试器调试。

对于工程巨大的,不容易定位问题,用堆栈回溯。

隔离代码。查看日志,版本比对

解决问题,找到问题之后要去解决。

8、遇到问题怎么办?

首先观察问题,遇到的是个什么样的问题,程序的逻辑问题,还是代码漏洞,出现野指针,内存溢出,没有释放,等等。问题定位了以后,就要想着如何解决问题,野指针初始化,释放后设为空指针,内存的临界点要控制好,也要记得释放内存,等等。

9、类成员的初始化有几种方式?有什么区别?为什么要初始化列表?

初始化类的成员有两种方式,一是使用初始化列表,二是在构造函数体内进行赋值操作。使用初始化列表主要是基于性能问题,对于内置类型,如int, float等,使用初始化类表和在构造函数体内初始化差别不是很大,但是对于类类型来说,最好使用初始化列表,为什么呢?由上面的测试可知,使用初始化列表少了一次调用默认构造函数的过程,这对于数据密集型的类来说,是非常高效的。

除了性能问题之外,有些时场合初始化列表是不可或缺的,以下几种情况时必须使用初始化列表

  • 常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面
  • 引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面
  • 没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化。

猜你喜欢

转载自blog.csdn.net/fanx021/article/details/80588312