C++面试题目

  • 运行下面的C++代码,打印的结果是什么?
View Code
class Base

{

public:

void print() { doPrint();}



private:

virtual void doPrint() {cout << "Base::doPrint" << endl;}

};



class Derived : public Base

{

private:

virtual void doPrint() {cout << "Derived::doPrint" << endl;}

};



int _tmain(int argc, _TCHAR* argv[])

{

Base b;

b.print();



Derived d;

d.print();

}

输出两行,分别是Base::doPrint和Derived::doPrint。在print中调用doPrint时,doPrint()的写法和this->doPrint()是等价的,因此将根据实际的类型调用对应的doPrint。所以结果是分别调用的是Base::doPrint和Derived::doPrint2。如果感兴趣,可以查看一下汇编代码,就能看出来调用doPrint是从虚函数表中得到函数地址的。

  • 运行如下的C代码,输出是什么?
View Code
char* GetString1()

{

char p[] = "Hello World";

return p;

}



char* GetString2()

{

char *p = "Hello World";

return p;

}





int _tmain(int argc, _TCHAR* argv[])

{

printf("GetString1 returns: %s. \n", GetString1());

printf("GetString2 returns: %s. \n", GetString2());



return 0;

}



答案:输出两行,第一行GetString1 returns: 后面跟的是一串随机的内容,而第二行GetString2 returns: Hello World. 两个函数的区别在于GetString1中是一个数组,而GetString2中是一个指针。

当运行到GetString1时,p是一个数组,会开辟一块内存,并拷贝"Hello World"初始化该数组。接着返回数组的首地址并退出该函数。由于p是GetString1内的一个局部变量,当运行到这个函数外面的时候,这个数组的内存会被释放掉。因此在_tmain函数里再去访问这个数组的内容时,结果是随机的。

当运行到GetString2时,p是一个指针,它指向的是字符串常量区的一个常量字符串。该常量字符串是一个全局的,并不会因为退出函数GetString2而被释放掉。因此在_tmain中仍然根据GetString2返回的地址得到字符串"Hello World"。

  • 运行如下的C++代码,输出是什么?
View Code
class A

{

public:

virtual void Fun(int number = 10)

{

std::cout << "A::Fun with number " << number;

}

};



class B: public A

{

public:

virtual void Fun(int number = 20)

{

std::cout << "B::Fun with number " << number;

}

};



int main()

{

B b;

A &a = b;

a.Fun();

}

答案:输出B::Fun with number 10。由于a是一个指向B实例的引用,因此在运行的时候会调用B::Fun。但缺省参数是在编译期决定的。在编译的时候,编译器只知道a是一个类型a的引用,具体指向什么类型在编译期是不能确定的,因此会按照A::Fun的声明把缺省参数number设为10。

            这一题的关键在于理解确定缺省参数的值是在编译的时候,但确定引用、指针的虚函数调用哪个类型的函数是在运行的时候。

  • RTTI 只有包含了虚函数的类才能使用。
  • dynamic_cast 当用于指针的时候,如果转换失败将返回空指针,而如果用于引用,因为没有空引用,所以将抛出bad_cast的异常
  • static_cast 和c 风格强转的区别: C的强制转换没有任何限制,可以随便转换,包括把不同类型的指针互相转换static_cast是C++里面的类型安全转换,这个转换不允许将毫无关系的两个数据类型的指针互相转化;如果需要那样的转化,必须得用reinterpret_cast,但是reinterpret_cast的可移植性很差很差,而且会引起很多的问题,像在同个基类的多个派生类之间相互转化
  • 运行下图中的C++代码,输出是什么?
    View Code
    #include <iostream>



    class A

    {

    private:

    int n1;

    int n2;

    public:

    A(): n2(0), n1(n2 + 2)

    {

    }



    void Print()

    {

    std::cout << "n1: " << n1 << ", n2: " << n2 << std::endl;

    }

    };



    int _tmain(int argc, _TCHAR* argv[])

    {

    A a;

    a.Print();



    return0;

    }
    答案:输出n1是一个随机的数字,n2为0。在C++中,成员变量的初始化顺序与变量在类型中的申明顺序相同,而与它们在构造函数的初始化列表中的顺序无关。因此在这道题中,会首先初始化n1,而初始n1的参数n2还没有初始化,是一个随机值,因此n1就是一个随机值。初始化n2时,根据参数0对其初始化,故n2=0。
  • 编译运行下图中的C++代码,结果是什么?(A)编译错误;(B)编译成功,运行时程序崩溃;(C)编译运行正常,输出10。请选择正确答案并分析原因。
    View Code
        #include <iostream>

    class A

    {

    private:

    int value;

    public:

    A(int n)

    {

    value = n;

    }

    A(A other)

    {

    value = other.value;

    }

    void Print()

    {

    std::cout << value << std::endl;}

    };

    int _tmain(int argc, _TCHAR* argv[])

    {

    A a = 10;

    A b = a;

    b.Print();

    return 0;

    }

答案:编译错误。在复制构造函数中传入的参数是A的一个实例。由于是传值,把形参拷贝到实参会调用复制构造函数。因此如果允许复制构造函数传值,那么会形成永无休止的递归并造成栈溢出。因此C++的标准不允许复制构造函数传值参数,而必须是传引用或者常量引用。在Visual Studio和GCC中,都将编译出错。

  • C++里的静态成员函数(不能用const的原因+static在c++中的第五种含义)

    static在c++中的第五种含义:用static修饰不访问非静态数据成员的类成员函数。这意味着一个静态成员函数只能访问它的参数、类的静态数据成员和全局变量。   不能用const的原因:一个静态成员函数访问的值是其参数、静态数据成员和全局变量,而这些数据都不是对象状态的一部分。而对成员函数中使用关键字 const是表明:函数不会修改该函数访问的目标对象的数据成员。既然一个静态成员函数根本不访问非静态数据成员,那么就没必要使用const了。

       什么时候使用静态数据成员和静态函数呢?

          定义数据成员为静态变量,以表明此全局数据逻辑上属于该类。

          定义成员函数为静态函数,以表明此全局函数逻辑上属于该类,而且该函数只对静态数据、全局数据或者参数进行操作,而不对非静态数据成员进行操作。

       static的第一种含义:修饰全局变量时,表明一个全局变量只对定义在同一文件中的函数可见。

       static的第二种含义:修饰局部变量时,表明该变量的值不会因为函数终止而丢失。

       static的第三种含义:修饰函数时,表明该函数只在同一文件中调用。

       static的第四种含义:修饰类的数据成员,表明对该类所有对象这个数据成员都只有一个实例。即该实例归 所有对象共有。

       static的第五种含义:修饰类成员函数,如上。

  • 运行下面中的代码,得到的结果是什么?
    View Code
    class A

    {

    private:

    int m_value;



    public:

    A(int value)

    {

    m_value = value;

    }

    void Print1()

    {

    printf("hello world");

    }

    virtualvoid Print2()

    {

    printf("hello world");

    }

    };



    int _tmain(int argc, _TCHAR* argv[])

    {

    A* pA = NULL;

    pA->Print1();

    pA->Print2();



    return0;

    }
    View Code
    class A

    {

    private:

    int m_value;



    public:

    A(int value)

    {

    m_value = value;

    }

    void Print1()

    {

    printf("hello world");

    }

    void Print2()

    {

    printf("%d", m_value);

    }

    };



    int _tmain(int argc, _TCHAR* argv[])

    {

    A* pA = NULL;

    pA->Print1();

    pA->Print2();



    return0;

    }
    分析:答案是Print1调用正常,打印出hello world,但运行至Print2时,程序崩溃。调用Print1时,并不需要pA的地址,因为Print1的函数地址是固定的。编译器会给Print1传入一个this指针,该指针为NULL,但在Print1中该this指针并没有用到。只要程序运行时没有访问不该访问的内存就不会出错,因此运行正常。在运行print2时,需要this指针才能得到m_value的值。由于此时this指针为NULL,因此程序崩溃了。

分析:答案是Print1调用正常,打印出hello world,但运行至Print2时,程序崩溃。Print1的调用情况和上面的题目一样,不在赘述。由于Print2是虚函数。C++调用虚函数的时候,要根据实例(即this指针指向的实例)中虚函数表指针得到虚函数表,再从虚函数表中找到函数的地址。由于这一步需要访问实例的地址(即this指针),而此时this指针为空指针,因此导致内存访问出错。

几种str的函数实现

View Code
char *strcat(char *strDest, const char *strScr) //将源字符串加const,表明其为输入参数
{
char * address = strDest; //该语句若放在assert之后,编译出错
assert((strDest != NULL) && (strScr != NULL)); //对源地址和目的地址加非0断言
while(*strDest) //是while(*strDest!=’\0’)的简化形式
{ //若使用while(*strDest++),则会出错,因为++是不受循环
strDest++; //约束的。所以要在循环体内++;因为要是*strDest最后指
} //向该字符串的结束标志’\0’。
while(*strDest++ = *strScr++)
{
NULL; //该循环条件内可以用++,
} //此处可以加语句*strDest=’\0’;有无必要?
return address; //为了实现链式操作,将目的地址返回
}
char *strcpy(char *strDest, const char *strScr)
{
char *address=strDest;
assert((strDest != NULL) && (strScr != NULL));
while(*strScr) //是while(*strScr != ’\0’)的简化形式;
{
*strDest++ = *strScr++;
}
*strDest = '\0'; //当strScr字符串长度小于原strDest字符串长度
return address; //时,如果没有改语句,就会出错了。
}
int strcmp (const char *str1,const char *str2)
{
int len = 0;
assert((str1 != '\0') && (str2 != '\0'));
while(*str1 && *str2 && (*str1 == *str2))
{
str1++;
str2++;
}
return *str1-*str2;
}
int strlen(const char *str)
{
int len = 0;
assert(str != NULL);
while(*str++)
{
len++;
}
return len;
}
内联函数和宏

内联函数和宏很类似,而区别在于,宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数,只是在需要用到的时候, 内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。你可以象调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题。  
        当然,内联函数也有一定的局限性。就是函数中的执行代码不能太多了,如果,内联函数的函数体过大,一般的编译器会放弃内联方式,而采用普通的方式调用函数。这样,内联函数就和普通函数执行效率一样了。  
        内联函数是不能为虚函数的,但样子上写成了内联的,即隐含的内联方式。在某种情况下,虽然有些函数我们声明为了所谓“内联”方式,但有时系统也会把它当作 普通的函数来处理,这里的虚函数也一样,虽然同样被声明为了所谓“内联”方式,但系统会把它当然非内联的方式来处理。  

 
 
  extern的原理很简单,就是告诉编译器:“你现在编译的文件中,有一个标识符虽然没有在本文件中定义,但是它是在别的文件中定义的全局变量,你要放行!”  我们为上面的错误程序加上extern关键字:  //A.cpp  extern int i;  void main()  {  i = 100; //试图使用B中定义的全局变量  }  //B.cpp  int i;  顺利通过编译,链接。

编辑本段函数

extern 函数1  常见extern放在函数的前面成为函数声明的一部分,那么,C语言关键字extern在函数的声明中起什么作用?  答案与分析:  如果函数的声明中带有关键字extern,仅仅是暗示这个函数可能在别的源文件里定义,没有其它作用。即下述两个函数声明没有明显的区别:  extern int f(); 和int f();  当然,这样的用处还是有的,就是在程序中取代include “*.h”来声明函数,在一些复杂的项目中,我比较习惯在所有的函数声明前添加extern修饰。  extern 函数2  当函数提供方单方面修改函数原型时,如果使用方不知情继续沿用原来的extern申明,这样编译时编译器不会报错。但是在运行过程中,因为少了或者多了输入参数,往往会造成系统错误,这种情况应该如何解决?  答案与分析:  目前业界针对这种情况的处理没有一个很完美的方案,通常的做法是提供方在自己的xxx_pub.h中提供对外部接口的声明,然后调用包涵该文件的头文件,从而省去extern这一步。以避免这种错误。  宝剑有双锋,对extern的应用,不同的场合应该选择不同的做法。
函数前加EXTERN 基本就等于没加。函数前加STATIC却是文件内可见。
static const都在类声明处就要设置好。
float 

由float类型与0比较学习浮点型编码格式

面试
虚函数解析
STL iterator的删除
http://apps.hi.baidu.com/share/detail/20376194
stack, deque, queue关系
sizeof 空对象大小
数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。
(1)修改内容上的差别
char a[] = “hello”;
a[0] = ‘X’;
char *p = “world”; // 注意p 指向常量字符串
p[0] = ‘X’; // 编译器不能发现该错误,运行时错误
void g(base & b){
   b.play;
  }
  void main(){
   son s;
   g(s);
   return;
  }

65.引用与指针有什么区别?

答 、1) 引用必须被初始化,指针不必。

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

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

70.什么函数不能声明为虚函数?

答 、constructor

80.全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?

答 、可以,在不同的C文件中以static形式来声明同名全局变量。

可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错
如何让一个函数在main函数之前执行!

98.下面的代码输出是什么,为什么?

void foo(void)

{

unsigned int a = 6;

int b = -20;

(a+b > 6) puts("> 6") : puts("<= 6");

}

 这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是 “>6”。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式 计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。

int a = 5, b = 7, c;

c = a+++b+a++;

写一个递归函数将内存中的字符串翻转"abc"->"cba",并且函数原型已确定:void reverse(char* p)

其实,要求越多,思路越确定,我的解如下:

#include <stdio.h>
#include <string.h>
char* s = "0123456789";
#define ENDL '\0'
void reverse(char* p){
       //这是这种方法的关键,使用static为的是能用str_reverse的思路,但是不好
       static char* x=0;
       if(x==0)x=p;
       char* q = x+strlen(p)-1;
       if(p==q)return;
       *q=(*p)^(*q);
       *p=(*p)^(*q);
       *q =(*p)^(*q);
       if(q==p+1)return;
       reverse(++p);
}

//这种方法就直观多了,但是当字符串很长的时候就很低效
void reverse2(char* p){
       if(*(p+1)==ENDL)return;
       for(char* o=p+strlen(p)-1,char t=*o;o!=p;o--)
          *o=*(o-1);
       *p=t;
       reverse2(p+1);
}

int main(int c,char** argv){
       reverse2(s);
       printf("%s\n",s);
       return 0;
}
实现http://www.cplusplus.com/reference/clibrary/cstring/strcat/
 STL有7种主要容器:vector,list,deque,map,multimap,set,multiset.
MVC模式是observer 模式的一个特例,典型的有MFC里面的文档视图架构。
char str1[] = "abc";

char str2[] = "abc";

const char str3[] = "abc";

const char str4[] = "abc";

const char* str5 = "abc";

const char* str6 = "abc";

cout << boolalpha << ( str1==str2 ) << endl; // 输出什么?

cout << boolalpha << ( str3==str4 ) << endl; // 输出什么?

cout << boolalpha << ( str5==str6 ) << endl; // 输出什么?

a. class B : public A { ……} // B公有继承自A,可以是间接继承的

b. class B { operator A( ); } // B实现了隐式转化为A的转化

c. class A { A( const B& ); } // A实现了non-explicit的参数为B(可以有其他带默认值的参数)构造函数

d. A& operator= ( const A& ); // 赋值操作,虽不是正宗的隐式类型转换,但也可以勉强算一个
数组作为函数参数后,就退化成指针了。。SIZEOF就变成了4了。。
64位http://apps.hi.baidu.com/share/detail/23929369#content
  • 字符串哈希函数

  (著名的ELFhash算法)  int ELFhash(char *key)  {  unsigned long h=0;  while(*key)  {  h=(h<<4)+*key++;  unsigned long g=h&0Xf0000000L;  if(g)  h^=g>>24;  h&=~g;  }  return h%MOD;  }
详细解说STL hash_map
hash_map 要实现hash<> equal_to<>
map 只要实现 operator< 就好了
 
http://www.360doc.com/content/11/0104/16/3700464_83906194.shtml
  •  在实际进入函数体前,参数++都已经计算完毕了。

 

View Code
 1 public static void test(int i)
 2         {
 3             return;
 4         }
 5         static void Main(string[] args)
 6         {
 7             //int[] op1 = new int[] { 3, 6, 4, 2 };
 8             //QS(op1, 0, op1.Length - 1);
 9 
10             //Print(op1);
11             int i = 1;
12             test(i++);
13 }

 

 

 

posted @ 2012-02-05 14:13  linyliny  阅读(129)  评论(0)    收藏  举报