C++

1. C 和 C++ 区别

    

2. const 有什么用途

    主要有三点:

      1:定义只读变量,即常量 

      2:修饰函数的参数和函数的返回值 

      3: 修饰函数的定义体,这里的函数为类的成员函数,被const修饰的成员函数代表不修改成员变量的值

 

3. 指针和引用的区别

    1:引用是变量的一个别名,内部实现是只读指针

    2:引用只能在初始化时被赋值,其他时候值不能被改变,指针的值可以在任何时候被改变

    3:引用不能为NULL,指针可以为NULL

    4:引用变量内存单元保存的是被引用变量的地址

    5:“sizeof 引用" = 指向变量的大小 , "sizeof 指针"= 指针本身的大小

    6:引用可以取地址操作,返回的是被引用变量本身所在的内存单元地址

    7:引用使用在源代码级相当于普通的变量一样使用,做函数参数时,内部传递的实际是变量地址

 

4. C++中有了malloc / free , 为什么还需要 new / delete     

  1,malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
  2,对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。
     对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。
     由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
  3,因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。

 

 

5. 编写类String 的构造函数,析构函数,拷贝构造函数和赋值函数

1、构造函数

         1、构造函数在构造对象时使用;
    2、传入参数的判断;
   3、对象的初始化问题。 
class String
{
public:
    String(const char *str = NULL); // 普通构造函数    
    String(const String &other);    // 拷贝构造函数
    ~String(void);                    // 析构函数    
    String & operator = (const String &other);    // 赋值函数
private:
    char *m_data;                    // 用于保存字符串
};

2、拷贝构造函数

         1、拷贝构造函数必须在构造对象时使用,即定义对象时;
   2、对象初始化问题
String::String(const String &other)
{
    int len = strlen(other.m_data);
    m_data = new char[len+1];
    strcpy(m_data,other.m_data);
}

3、赋值函数

         1、赋值函数使用时,对象肯定已经建立;
   2、赋值前,判断是否是自我赋值;
   3、赋值前,内存空间的准备:
       由于赋值前,对象已占有一定大小内存,但是赋值对象所占内存大小与
       对象已占的内存大小不一定一致;
       先释放对象已占的内存,然后分配心内存。
   4、正常赋值
String & String::operator = (const String &other)
{
    if (&other == this)
    {
        return *this;
    }
    
    delete [] m_data;
    int len = strlen(other.m_data);
    m_data = new char[len+1];
    strcpy(m_data,other.m_data);
    
    return *this;
}

4、析构函数  

      资源的释放
String::~String(void)
{
    delete []m_data;
}


6. 多态的实现

多态是OOP中一个十分重要的特性,至于如何使用可以参考这篇C++与JAVA多态相同与不同,相信对于学习C++和java的人都有帮助。

多态实现的关键技术是动态绑定。

动态绑定:程序在运行期间寻找函数地址

静态绑定:代码在编译时已经确定了函数地址

 

多态的实现表象是指针+虚函数,本质是虚表+虚指针。

这里有一篇博客写的很全面,可以参考一下c++多态实现的机制

 

1.虚表

声明了虚函数的类会隐式创建一个虚指针指向虚表,而虚表保存了虚函数的地址(虚表中只有虚函数,非虚函数在另一个表中),当我们调用虚函数时,实际上是通过虚指针去查找虚表中对应虚函数的地址

2.虚指针

虚指针指向类的虚表,属于类的成员变量,可以继承,当子类继承父类的虚指针时,子类的虚指针指向子类的虚表。虚表也可以继承,也可以理解为继承了虚函数,然后创建了自己的虚表,所以如果没有重定义父类虚函数,那么子类虚表和父类虚表完全相同;重定义的虚函数在子类虚表中的地址会改变。

3.虚指针的初始化

虚指针在构造函数中进行初始化,指向类的虚表。如果类B继承了类A,那么我们知道生成B的实例对象时,会先调用A的构造函数,此时虚指针指向A的虚表,然后调用B的构造函数,此时虚指针指向B的虚表,所以当B的实例构造完成后,虚指针指向B的虚表。

4.多态

用下面的例子讲解,当我们声明一个父类A的指针p指向子类B的对象时,在编译阶段是不分配内存的(不构造对象的),如果想了解编译过程做了什么,可以参考一下这篇C程序编译过程浅析。也就是说编译器并不知道指针p指向的是B类的对象,只知道这是一个A类的指针,那么在编译p->func()时,会直接查找A中func的地址并替换(可以简单理解为这样),也就是静态绑定,那么在运行时自然调用的就是A的func了

class A
{
public:
    A(){};
    void func()
    {
        cout << "A func called!";
    }
};

class B:public A
{
public:
    B(){};
    void func()
    {
        cout << "B func called!";
    }

};

int main()
{
    A*p = new B();
    p->func();
    return 0;
}
但是如果将func声明为虚函数,如下,那么在编译时编译器一看func是虚函数,会直接跳过,那么在运行时,B的对象已经被构造出来了,那么p所指向的B对象的虚指针已经指向了B的虚表,此时调用p->func()时,操作系统会在p的虚表中去寻找func这个函数,也就是动态绑定,然后调用,这时候调用的自然就是B的func了,可以说虚指针和虚表就是为了多态而生的
class A
{
public:
    A(){};
    virtual  void func()
    {
        cout << "A func called!";
    }
};

class B:public A
{
public:
    B(){};
    void func()
    {
        cout << "B func called!";
    }

};

int main()
{
    A*p = new B();
    p->func();
    return 0;
}


7. 单链表的逆置

 

8. 堆和栈的区别   

  一个由c/C++编译的程序占用的内存分为以下几个部分 
  1、栈区(stack)―   由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 
  2、堆区(heap) ―   一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。
     注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。 
  3、全局区(静态区)(static)―,全局变量和静态变量的存储是放在一块的,
     初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放 
  4、文字常量区  ―常量字符串就是放在这里的。 程序结束后由系统释放 
  5、程序代码区―存放函数体的二进制代码。

 

 

10. 不调用C/C++ 的字符串库函数,编写strcpy

   char * strcpy(char * strDest,const char * strSrc)
        {
                if ((strDest==NULL)||strSrc==NULL))                     
                   return NULL;    
                char * strDestCopy=strDest; 
                while ((*strDest++=*strSrc++)!='\0'); 
                *strDest = '\0';
                return strDestCopy;
        }

 

 

11. 关键字static的作用

    1.  函数体内 static 变量的作用范围为该函数体,不同于 auto 变量, 该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值

    2.  在模块内的 static 全局变量可以被模块内所有函数访问,但不能被模块外其他函数访问

    3.  在模块内的static 函数只可被这一模块内的其他函数调用,这个函数的使用范围被限制在声明它的模块内

    4.  在类的static 成员变量属于整个类所拥有,对类的所以对象只有一份拷贝

    5.  在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的 static 成员变量

   

     介绍它最重要的一条:隐藏。(static函数,static变量均可) --> 对应上面的2、3项
        当同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。
        举例来说明。同时编译两个源文件,一个是a.c,另一个是main.c。

   //a.c
    char a = 'A';               // global variable
    void msg()
    {
      printf("Hello\n");
    }
复制代码
  //main.c
   int main()
   {
     extern char a;       // extern variable must be declared before use
     printf("%c ", a);
     (void)msg();
     return 0;
   }
复制代码

       程序的运行结果是:

     A Hello

 

 

      为什么在a.c中定义的全局变量a和函数msg能在main.c中使用?

      前面说过,所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。此例中,a是全局变量,msg是函数,并且都没有加static前缀,

        因此对于另外的源文件main.c是可见的。

     如果加了static,就会对其它源文件隐藏。例如在a和msg的定义前加上static,main.c就看不到它们了。

     利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏

 

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

     C++语言支持函数重载,C语言不支持函数重载,函数被C++编译器编译后在库中的名字与C语言的不同,

      假设某个函数原型为:

  1.           void foo(int x, inty);
    该函数被C编译器编译后在库中的名字为:  _foo
    而C++编译器则会产生像: _foo_int_int   之类的名字。
    为了解决此类名字匹配的问题,C++提供了C链接交换指定符号 extern "C"。

 

 

13. 头文件种的ifndef/define/endif 是干什么用的

      防止头文件被重复包含

 

14. 线程和进程的联系和区别

      http://blog.csdn.NET/wolenski/article/details/7969908

 

15. 线程有哪几种状态

     http://blog.csdn.Net/wolenski/article/details/7969908

 

16. 进程间的通信方式

     管道、有名管道、信号、共享内存、消息队列、信号量、套接字、文件.

 

17. 线程同步和线程互斥的区别

    http://blog.youkuaiyun.com/wolenski/article/details/7969908

 

18. 线程同步的方式

     Linux:   互斥锁、条件变量和信号量

     http://blog.youkuaiyun.com/zsf8701/article/details/7844316

 

19. 网络七层

    

20. TCP和UDP有什么区别

     TCP---传输控制协议,提供的是面向连接、可靠的字节流服务。

                 当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。

                 TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。

     UDP---用户数据报协议,是一个简单的面向数据报的运输层协议。

                 UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。

                 由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快

 

21. 编写socket套接字的步骤

 

22. TCP三次握手和四次挥手, 以及各个状态的作用

     http://hi.baidu.com/suxinpingtao51/item/be5f71b3a907dbef4ec7fd0e?qq-pf-to=pcqq.c2c

 

23. HTTP协议

            http(超文本传输协议)是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式,

     HTTP1.1版本中给出一种持续连接的机制,绝大多数的Web开发,都是构建在HTTP协议之上的Web应用。

    TCP 和 HTTP区别: http://blog.youkuaiyun.com/lemonxuexue/article/details/4485877

 

24. 使用过的 shell 命令

       cp , mv , rm , mkdir , touch , pwd , cd  , ls , top , cat , tail , less , df , du , man , find , kill , sudo , cat 

 

25. 使用过的 vim 命令

       wq!, dd , dw , yy , p , i , %s/old/new/g , /abc 向后搜索字符串abc , ?abc向前搜索字符串abc

 

26. 使用过的 gdb 命令

      http://blog.youkuaiyun.com/dadalan/article/details/3758025

 

27. 常见算法

       快速排序、堆排序和归并排序

       堆排序 : http://blog.youkuaiyun.com/xiaoxiaoxuewen/article/details/7570621

       快速排序、归并排序: http://blog.youkuaiyun.com/morewindows/article/details/6684558

       稳定性分析 http://baike.baidu.com/link?url=ueoZ3sNIOvMNPrdCKbd8mhfebC85B4nRc-7hPEJWi-hFo5ROyWH2Pxs9RtvLFRJL

 

28. C库函数实现

 

29. 静态链表和动态链表的区别

      http://blog.youkuaiyun.com/toonny1985/article/details/4868786

 

31. 大并发( epoll )

       优点:

             http://blog.youkuaiyun.com/sunyurun/article/details/8194979

       实例:

             http://www.cnblogs.com/ggjucheng/archive/2012/01/17/2324974.html

       

32. 海量数据处理的知识点,(hash表, hash统计)

    hash表: http://hi.baidu.com/05104106/item/62736054402852c09e26679b

    海量数据处理方法: http://blog.youkuaiyun.com/v_july_v/article/details/7382693

 

 

33. 什么时候要用虚析构函数

       通过基类的指针来删除派生类的对象时,基类的析构函数应该是虚的。否则其删除效果将无法实现。

       一般情况下,这样的删除只能够删除基类对象,而不能删除子类对象,形成了删除一半形象,从而千万内存泄漏。

      原因:

              在公有继承中,基类对派生类及其对象的操作,只能影响到那些从基类继承下来的成员。

              如果想要用基类对非继承成员进行操作,则要把基类的这个操作(函数)定义为虚函数。
              那么,析构函数自然也应该如此:如果它想析构子类中的重新定义或新的成员及对象,当然也应该声明为虚的。

      注意:

      如果不需要基类对派生类及对象进行操作,则不能定义虚函数(包括虚析构函数),因为这样会增加内存开销。

 

34. c++怎样让返回对象的函数不调用拷贝构造函数

    拷贝构造函数前加 “explicit” 关键字

35. 孤儿进程和僵尸进程

   http://www.cnblogs.com/Anker/p/3271773.html

 

各大计算机公司 笔试及面试 题目

http://blog.youkuaiyun.com/huyfaeng/article/category/880022

 

面试题有难有易,不能因为容易,我们就轻视,更不能因为难,我们就放弃。我们面对高薪就业的态度永远不变,那就是坚持、坚持、再坚持。出现问题,找原因;遇到困难,想办法。我们一直坚信只有在坚持中才能看到希望,而不是看到希望才去坚持。

人生没有如果,只有结果和后果。既然选择了,就不后悔。年轻就是资本,年轻就要吃苦,就要历练。就要学会在坚持中成长。如此感慨,至深的心得体会,绝对的经验之谈。

OK,进入正题,下面就是《必须掌握的20道技术面试题》。

问1:请用简单的语言告诉我C++ 是什么?

答:C++是在C语言的基础上开发的一种面向对象编程语言,应用广泛。C++支持多种编程范式 --面向对象编程、泛型编程和过程化编程。 其编程领域众广,常用于系统开发,引擎开发等应用领域,是最受广大程序员受用的最强大编程语言之一,支持类:类、封装、重载等特性!

问2:C和C++的区别?

答:c++在c的基础上增添类,C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程(事务)控制),而对于C++,首要考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题域,这样就可以通过获取对象的状态信息得到输出或实现过程(事务)控制。

问3:什么是面向对象(OOP)?

答:面向对象是一种对现实世界理解和抽象的方法、思想,通过将需求要素转化为对象进行问题处理的一种思想。

问4:什么是多态?

答:多态是指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果。不同的对象,收到同一消息可以产生不同的结果,这种现象称为多态。

问5:设计模式懂嘛,简单举个例子?

答:设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。

比如单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。

适用于:当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时;当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。

比如工厂模式,定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。

适用于:当一个类不知道它所必须创建的对象的类的时候;当一个类希望由它的子类来指定它所创建的对象的时候;当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。

问6:STL库用过吗?常见的STL容器有哪些?算法用过哪几个?

答:STL包括两部分内容:容器和算法。(重要的还有融合这二者的迭代器)

容器,即存放数据的地方。比如array等。

在STL中,容器分为两类:序列式容器和关联式容器。

序列式容器,其中的元素不一定有序,但都可以被排序。如:vector、list、deque、stack、queue、heap、priority_queue、slist;

关联式容器,内部结构基本上是一颗平衡二叉树。所谓关联,指每个元素都有一个键值和一个实值,元素按照一定的规则存放。如:RB-tree、set、map、multiset、multimap、hashtable、hash_set、hash_map、hash_multiset、hash_multimap。

下面各选取一个作为说明。

vector:它是一个动态分配存储空间的容器。区别于c++中的array,array分配的空间是静态的,分配之后不能被改变,而vector会自动重分配(扩展)空间。

set:其内部元素会根据元素的键值自动被排序。区别于map,它的键值就是实值,而map可以同时拥有不同的键值和实值。

算法,如排序,复制……以及个容器特定的算法。这点不用过多介绍,主要看下面迭代器的内容。

迭代器是STL的精髓,我们这样描述它:迭代器提供了一种方法,使它能够按照顺序访问某个容器所含的各个元素,但无需暴露该容器的内部结构。它将容器和算法分开,好让这二者独立设计。

问7:数据结构会吗?项目开发过程中主要用到那些?

答:数据结构中主要会用到数组,链表,树(较少),也会用到栈和队列的思想。

问8:const知道吗?解释其作用。

答:

1.const 修饰类的成员变量,表示成员常量,不能被修改。

2.const修饰函数承诺在本函数内部不会修改类内的数据成员,不会调用其它非 const 成员函数。

3.如果 const 构成函数重载,const 对象只能调用 const 函数,非 const 对象优先调用非 const 函数。

4.const 函数只能调用 const 函数。非 const 函数可以调用 const 函数。

5.类体外定义的 const 成员函数,在定义和声明处都需要 const 修饰符。。

问9:类的static变量在什么时候初始化?函数的static变量在什么时候初始化?

答:类的静态成员变量在类实例化之前就已经存在了,并且分配了内存。函数的static变量在执行此函数时进行初始化。

问10:堆和栈的区别?堆和栈的生命周期?

答:

一、堆栈空间分配区别:

1、栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈;

2、堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。

二、堆栈缓存方式区别:

1、栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放;

2、堆是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。

三、堆栈数据结构区别:

堆(数据结构):堆可以被看成是一棵树,如:堆排序;

栈(数据结构):一种先进后出的数据结构。

问11:C和C++的区别?

答:C++在C的基础上增添类

C是一个结构化语言,它的重点在于算法和数据结构。

C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程(事务)控制),而对于C++,首要考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题域,这样就可以通过获取对象的状态信息得到输出或实现过程(事务)控制。

 

问12:解释下封装、继承和多态?

答:

一、封装:

封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(我们称之为类)。

封装的意义在于保护或者防止代码(数据)被我们无意中破坏。

二、继承:

继承主要实现重用代码,节省开发时间。

子类可以继承父类的一些东西。

三、多态

多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。

问13:指针和引用的区别?

答:

1. 指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元;而引用仅是个别名;

2. 引用使用时无需解引用(*),指针需要解引用;

3. 引用只能在定义时被初始化一次,之后不可变;指针可变;

4. 引用没有 const,指针有 const;

5. 引用不能为空,指针可以为空;

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

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

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

9.从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。

问14:什么是内存泄漏?面对内存泄漏和指针越界,你有哪些方法?你通常采用哪些方法来避免和减少这类错误?

答:用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元即为内存泄露。

使用的时候要记得指针的长度。

malloc的时候得确定在那里free.

对指针赋值的时候应该注意被赋值指针需要不需要释放.

动态分配内存的指针最好不要再次赋值.

问15:常用的排序算法有哪些?简单描述几个排序算法的优缺点?

答:选择、冒泡、快速、**、希尔、归并、堆排等。

1.快排:是冒泡排序的一种改进。

优点:快,数据移动少

缺点:稳定性不足

2.归并:分治法排序,稳定的排序算法,一般用于对总体无序,但局部有序的数列。

优点:效率高O(n),稳定

缺点:比较占用内存

问16:new和malloc的区别?

答:

1、malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。

2、对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。

3、由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。

4、C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。

5、new可以认为是malloc加构造函数的执行。new出来的指针是直接带类型信息的。而malloc返回的都是void指针。

问17:TCP和UDP通信的差别?什么是IOCP?

答:

1.TCP面向连接, UDP面向无连接的

2.TCP有保障的,UDP传输无保障的

3.TCP是效率低的,UDP效率高的

4.TCP是基于流的,UDP基于数据报文

5.TCP传输重要数据,UDP传输不重要的数据

IOCP全称I/O Completion Port,中文译为I/O完成端口。

IOCP是一个异步I/O的API,它可以高效地将I/O事件通知给应用程序。

与使用select()或是其它异步方法不同的是,一个套接字[socket]与一个完成端口关联了起来,然后就可继续进行正常的Winsock操作了。然而,当一个事件发生的时候,此完成端口就将被操作系统加入一个队列中。然后应用程序可以对核心层进行查询以得到此完成端口。

问18:同步IO和异步IO的区别?

答:

A. 同步

所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。

按照这个定义,其实绝大多数函数都是同步调用(例如sin isdigit等)。

但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。

最常见的例子就是 SendMessage。

该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。

当对方处理完毕以后,该函数才把消息处理函数所返回的值返回给调用者。

B. 异步

异步的概念和同步相对。

当一个异步过程调用发出后,调用者不会立刻得到结果。

实际处理这个调用的部件是在调用发出后,通过状态、通知来通知调用者,或通过回调函数处理这个调用。

问19:解释C++中静态函数和静态变量?

答:

(1)类静态数据成员在编译时创建并初始化:在该类的任何对象建立之前就存在,不属于任何对象,而非静态类成员变量则是属于对象所有的。类静态数据成员只有一个拷贝,为所有此类的对象所共享。

(2)类静态成员函数属于整个类,不属于某个对象,由该类所有对象共享。

1、static 成员变量实现了同类对象间信息共享。

2、static 成员类外存储,求类大小,并不包含在内。

3、static 成员是命名空间属于类的全局变量,存储在 data 区的rw段。

4、static 成员只能类外初始化。

5、可以通过类名访问(无对象生成时亦可),也可以通过对象访问。

1、静态成员函数的意义,不在于信息共享,数据沟通,而在于管理静态数据成员,完成对静态数据成员的封装。

2、静态成员函数只能访问静态数据成员。原因:非静态成员函数,在调用时 this指针时被当作参数传进。而静态成员函数属于类,而不属于对象,没有 this 指针。

问20:说下你对内存的了解?

答:

1.栈 - 由编译器自动分配释放

2.堆 - 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收

3.全局区(静态区),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。- 程序结束释放

4.另外还有一个专门放常量的地方。- 程序结束释放

5 程序代码区,存放2进制代码。

在函数体中定义的变量通常是在栈上,用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。在所有函数体外定义的是全局量,加了static修饰符后不管在哪里都存放在全局区(静态区),在所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件用,在函数体内定义的static表示只在该函数体内有效。另外,函数中的"adgfdf"这样的字符串存放在常量区。




1.new、delete、malloc、free关系

delete会调用对象的析构函数,和new对应free只会释放内存,new调用构造函数。malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。

2.delete与 delete []区别

delete只会调用一次析构函数,而delete[]会调用每一个成员的析构函数。在More Effective C++中有更为详细的解释:“当delete操作符用于数组时,它为每个数组元素调用析构函数,然后调用operator delete来释放内存。”delete与new配套,delete []与new []配套

MemTest *mTest1=new MemTest[10];

MemTest *mTest2=new MemTest;

Int *pInt1=new int [10];

Int *pInt2=new int;

delete[]pInt1; //-1-

delete[]pInt2; //-2-

delete[]mTest1;//-3-

delete[]mTest2;//-4-

在-4-处报错。

这就说明:对于内建简单数据类型,delete和delete[]功能是相同的。对于自定义的复杂数据类型,delete和delete[]不能互用。delete[]删除一个数组,delete删除一个指针。简单来说,用new分配的内存用delete删除;用new[]分配的内存用delete[]删除。delete[]会调用数组元素的析构函数。内部数据类型没有析构函数,所以问题不大。如果你在用delete时没用括号,delete就会认为指向的是单个对象,否则,它就会认为指向的是一个数组。

3.C++有哪些性质(面向对象特点)

封装,继承和多态。

4.子类析构时要调用父类的析构函数吗?

析构函数调用的次序是先派生类的析构后基类的析构,也就是说在基类的的析构调用的时候,派生类的信息已经全部销毁了。定义一个对象时先调用基类的构造函数、然后调用派生类的构造函数;析构的时候恰好相反:先调用派生类的析构函数、然后调用基类的析构函数。

5.多态,虚函数,纯虚函数

多态:是对于不同对象接收相同消息时产生不同的动作。C++的多态性具体体现在运行和编译两个方面:在程序运行时的多态性通过继承和虚函数来体现;

在程序编译时多态性体现在函数和运算符的重载上;

虚函数:在基类中冠以关键字 virtual 的成员函数。 它提供了一种接口界面。允许在派生类中对基类的虚函数重新定义。

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

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

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

6.求下面函数的返回值(微软)

int func(x) 

int countx = 0; 

while(x) 

countx ++; 

x = x&(x-1); 

return countx; 

假定x = 9999。 答案:8

思路:将x转化为2进制,看含有的1的个数。

7.什么是“引用”?申明和使用“引用”要注意哪些问题?

答:引用就是某个目标变量的“别名”(alias),对应用的操作与对变量直接操作效果完全相同。申明一个引用的时候,切记要对其进行初始化。引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。不能建立数组的引用。

8.将“引用”作为函数参数有哪些特点?

(1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。

(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。

(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。

9.在什么时候需要使用“常引用”? 

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

例1

int a ;

const int &ra=a;

ra=1; //错误

a=1; //正确

例2

string foo( );

void bar(string & s);

那么下面的表达式将是非法的:

bar(foo( ));

bar("hello world");

原因在于foo( )和"hello world"串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。引用型参数应该在能被定义为const的情况下,尽量定义为const 。

10.将“引用”作为函数返回值类型的格式、好处和需要遵守的规则?

 

格式:类型标识符 &函数名(形参列表及类型说明){ //函数体 }

好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtime error! 

注意事项:

(1)不能返回局部变量的引用。这条可以参照Effective C++[1]的Item 31。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。

(2)不能返回函数内部new分配的内存的引用。这条可以参照Effective C++[1]的Item 31。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。

(3)可以返回类成员的引用,但最好是const。这条原则可以参照Effective C++[1]的Item 30。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。

(4)流操作符重载返回值申明为“引用”的作用:

流操作符<<和>>,这两个操作符常常希望被连续使用,例如:cout << "hello" << endl; 因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括:返回一个流对象和返回一个流对象指针。但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是C++语言中引入引用这个概念的原因吧。 

赋值操作符=。这个操作符象流操作符一样,是可以连续使用的,例如:x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。

 

#include<iostream.h>

int &put(int n);

int vals[10];

int error=-1;

void main()

{

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

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

cout<<vals[0]; 

cout<<vals[9];

int &put(int n)

{

if (n>=0 && n<=9 ) return vals[n]; 

else { cout<<"subscript error"; return error; }

}

(5)在另外的一些操作符中,却千万不能返回引用:+-*/ 四则运算符。它们不能返回引用,Effective C++[1]的Item23详细的讨论了这个问题。主要原因是这四个操作符没有side effect,因此,它们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部变量的引用,返回一个new分配的对象的引用、返回一个静态对象引用。根据前面提到的引用作为返回值的三个规则,2、3两个方案都被否决了。静态对象的引用又因为((a+b) == (c+d))会永远为true而导致错误。所以可选的只剩下返回一个对象了。

 

11、结构与联合有和区别?

 

(1). 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。 

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

12、试写出程序结果:

int  a=4;

int  &f(int  x)

{    a=a+x;

      return  a;

}

int main(void)

{    int   t=5;

     cout<<f(t)<<endl;  a = 9

    f(t)=20;             a = 20

    cout<<f(t)<<endl;     t = 5,a = 20  a = 25

     t=f(t);                a = 30 t = 30

    cout<<f(t)<<endl;  }    t = 60

}

13.重载(overload)和重写(overried,有的书也叫做“覆盖”)的区别?

常考的题目。从定义上来说:

重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

重写:是指子类重新定义父类虚函数的方法。

从实现原理上来说:

重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!

重写:和多态真正相关。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚绑定)。

  

14.有哪几种情况只能用intialization list 而不能用assignment?

答案:当类中含有const、reference 成员变量;基类的构造函数都需要初始化表。

15. C++是不是类型安全的?

答案:不是。两个不同类型的指针之间可以强制转换(用reinterpret cast)。C#是类型安全的。

16. main 函数执行以前,还会执行什么代码?

答案:全局对象的构造函数会在main 函数之前执行。

17. 描述内存分配方式以及它们的区别?

1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。

2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。

3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。

 18.分别写出BOOL,int,float,指针类型的变量a 与“零”的比较语句。

答案:

BOOL : if ( !a ) or if(a)

int : if ( a == 0)

float : const EXPRESSION EXP = 0.000001

if ( a < EXP && a >-EXP)

pointer : if ( a != NULL) or if(a == NULL)

19.请说出const与#define 相比,有何优点?

答案:

const作用:定义常量、修饰函数参数、修饰函数返回值三个作用。被Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。

1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。

2) 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。

20.简述数组与指针的区别?

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。

(1)修改内容上的差别

char a[] = “hello”;

a[0] = ‘X’;

char *p = “world”; // 注意p 指向常量字符串

p[0] = ‘X’; // 编译器不能发现该错误,运行时错误

(2) 用运算符sizeof 可以计算出数组的容量(字节数)。sizeof(p),p 为指针得到的是一个指针变量的字节数,而不是p 所指的内存容量。C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。

char a[] = "hello world";

char *p = a;

cout<< sizeof(a) << endl; // 12 字节

cout<< sizeof(p) << endl; // 4 字节

计算数组和指针的内存容量

void Func(char a[100])

{

cout<< sizeof(a) << endl; // 4 字节而不是100 字节

}

21题: int (*s[10])(int) 表示的是什么?

int (*s[10])(int) 函数指针数组,每个指针指向一个int func(int param)的函数。

 

22题:栈内存与文字常量区

 

           char str1[] = "abc";
  char str2[] = "abc";

  const char str3[] = "abc";
  const char str4[] = "abc";

  const char *str5 = "abc";
  const char *str6 = "abc";

  char *str7 = "abc";
  char *str8 = "abc";

  cout << ( str1 == str2 ) << endl;//0  分别指向各自的栈内存
  cout << ( str3 == str4 ) << endl;//0  分别指向各自的栈内存
  cout << ( str5 == str6 ) << endl;//1指向文字常量区地址相同

  cout << ( str7 == str8 ) << endl;//1指向文字常量区地址相同

  结果是:0 0 1 1

  解答:str1,str2,str3,str4是数组变量,它们有各自的内存空间;而str5,str6,str7,str8是指针,它们指向相同的常量区域。

 

 

23题:将程序跳转到指定内存地址

要对绝对地址0x100000赋值,我们可以用(unsigned int*)0x100000 = 1234;那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?


  *((void (*)( ))0x100000 ) ( );
  首先要将0x100000强制转换成函数指针,即:
  (void (*)())0x100000
  然后再调用它:
  *((void (*)())0x100000)();
  用typedef可以看得更直观些:
  typedef void(*)() voidFuncPtr;
  *((voidFuncPtr)0x100000)();

 

24题:int id[sizeof(unsigned long)];这个对吗?为什么?


 答案:正确 这个 sizeof是编译时运算符,编译时就确定了  ,可以看成和机器有关的常量。

 

 

25题:引用与指针有什么区别?


【参考答案】                         
1) 引用必须被初始化,指针不必。

2) 引用初始化以后不能被改变,指针可以改变所指的对象。

3) 不存在指向空值的引用,但是存在指向空值的指针。

 

 

26题:const  与 #define 的比较 ,const有什么优点?


 【参考答案】

(1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应) 。

(2)  有些集成化的调试工具可以对 const 常量进行调试,但是不能对宏常量进行调试。

 

 

 

27题:复杂声明

void * ( * (*fp1)(int))[10];

float (*(* fp2)(int,int,int))(int);

int (* ( * fp3)())[10]();

分别表示什么意思?
【标准答案】                                                           

1.void * ( * (*fp1)(int))[10];   fp1是一个指针,指向一个函数,这个函数的参数为int型,函数的返回值是一个指针,这个指针指向一个数组,这个数组有10个元素,每个元素是一个void*型指针。

2.float (*(* fp2)(int,int,int))(int);   fp2是一个指针,指向一个函数,这个函数的参数为3个int型,函数的返回值是一个指针,这个指针指向一个函数,这个函数的参数为int型,函数的返回值是float型。

3.int (* ( * fp3)())[10]();   fp3是一个指针,指向一个函数,这个函数的参数为空,函数的返回值是一个指针,这个指针指向一个数组,这个数组有10个元素,每个元素是一个指针,指向一个函数,这个函数的参数为空,函数的返回值是int型。

 

 

 

28题:内存的分配方式有几种?

【参考答案】

一、从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量。

二、在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

三、从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。

 

 

29题:基类的析构函数不是虚函数,会带来什么问题?

【参考答案】派生类的析构函数用不上,会造成资源的泄漏。

 

30题:全局变量和局部变量有什么区别?是怎么实现的?操作系统和编译器是怎么知道的?

【参考答案】

生命周期不同:

全局变量随主程序创建和创建,随主程序销毁而销毁;局部变量在局部函数内部,甚至局部循环体等内部存在,退出就不存在;

使用方式不同:通过声明后全局变量程序的各个部分都可以用到;局部变量只能在局部使用;分配在栈区。 

操作系统和编译器通过内存分配的位置来知道的,全局变量分配在全局数据段并且在程序开始运行的时候被加载。局部变量则分配在堆栈里面 。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值