C++ 笔试题集锦(1)

来源:互联网 发布:linux项目新手 编辑:程序博客网 时间:2024/06/16 23:03

本篇blog旨在收集平时遇到的一些“稀奇古怪”的面试题。


问题1

问题表述:
在C++中,为了让某个类只能通过new来创建(即如果直接创建栈上对象,编译器将报错),应该如何做?

解析:
将析构函数设为私有。
因为new的过程中,编译器不会检测析构函数是否可访问,但要注意的是,这将导致不能调用delete删除该对象所占用的内存,所以需要在该类的内部自己增加一个成员函数,在该自定义函数中使用delete this的方式清除对象的内存。


问题2

问题表述:
如下一段代码的执行结果是?

#include <iostream>using namespace std;class A{public:    virtual void fun(int a = 1){        cout << "A->" << a << endl;    }    virtual void test(){        fun();    }};class B : public A{public:    virtual void fun(int a = 0){       cout << "B->" << a <<endl;    }};int main(){    B * p = new B;    p->test();    return 0;}

解析:
由于p指向了派生类B的对象,所以,在调用A中的test之后,虚函数func()会调用派生类B的函数,而默认形参仍然会使用test所在的A类的形参1,从而输出B->1.


问题3

问题表述:
代码如下,bar1,bar2和bar3,哪个函数将编译出错:

class A{public:   virtual void foo();}class B{public:   virtual void foo();}class C : public A, public B{public:   virtual void foo();}void bar1(A * pa){    B * pc = dynamic_cast<B*>(pa);}void bar2(A * pa){    B * pc = static_cast<B*>(pa);}void bar3(){    C c;    A * pa = &c;    B * pc = static_cast<B*>(static_cast<C*>(pa));}

解析:
static_cast和dynamic_cast都是用于强制类型转换。

dynamic_cast是在运行时遍历继承树,所以在编译时不会报错。但是类A,B无关,所以在运行时报错。

static_cast,编译器隐式执行任何类型转换都可由它显示完成。但是对于:
1)内置类型:比如可由将int转换为double(编译器会执行隐式转换),但是不能将int*转换为double*。
2)对于用户自定义类型,如果类型无关,则会编译出错。如果存在继承关系,则可由在基类和派生类之间进行任何转换,在编译期间不会出错。


问题4

问题表述:
假设在一个 32 位 little endian 的机器上运行下面的程序,结果是多少?

#include <stdio.h>int main(){  long long a = 1, b = 2, c = 3;   printf("%d %d %d\n", a, b, c);   return 0;}

解析:
小端序 : 低位存低字节。
1) C/C++的函数参数入栈方式 默认是__cdcall, 从右到做依次入栈,所以首先入栈的是c(0x03)。
2) 栈的生长方向是从高往低的
3) %d格式输出的是4个字节大小,而long long为8个字节
具体在内存中的排列方式如下:

这里写图片描述

注: 以上问题用 gcc 编译结果为 1 0 2,用VS编译结果为1 2 3。


问题5

问题表述:
建立派生类对象时,3种构造函数分别是a(基类的构造函数)、b(成员对象的构造函数)、c(派生类的构造函数)这3种构造函数的调用顺序为

解析:
a b c

class A{public:  A(){      cout << "A" <<endl;  }};class B{public:    B(){      cout << "B" <<endl;    }};class C : public A{public:  C(): A(){ //A如果没有默认构造函数,则必须在C的成员初始化列表中显式调用基类的构造函数      cout << "C" <<endl;  }  B m_b;};C c;

执行派生类构造函数的顺序是:

  • 调用基类构造函数;
  • 调用内嵌成员对象的构造函数,调用顺序按照他们在类中声明的顺序;
  • 再执行派生类构造函数本身。
  • 析构函数的调用顺序相反。

如果把B的构造函数修改为:

B(int); //也就是此时,B没有默认构造函数.

从以下结果可以看出,如果B有默认构造函数,那么将在C的成员初始化列表中默认添加。
这里写图片描述


问题6

问题表述:
x是一个行列数均为1000二维数组,下面代码效率执行最高(最快执行完)的是:

1)for(int j=0;j<1000;j++) for(int i=0;i<1000;i++) x[i][j]+=x[j][i];2)for(int i=0;i<1000;j++) for(int j=0;j<1000;j++) x[i][j]+=x[j][i];3)for(int i=0;i<1000;j++) for(int j=0;j<1000;j++) x[j][i]+=x[j][i];4)for(int i=0;i<1000;i++) for(int j=0;j<1000;j++) x[i][j]+=x[i][j];

解析:
考察了CPU cache的预取操作,数组x[1000][1000]在内存中,其实是按行进行存储。4)选项外部循环是按行进行,因此操作第i行时,会将第i行后面的部分数预取到cache中(每一次读取数据都是读取一个cache line,如果需要的数据已经在cache中,那么将不会从main memory中读取),所以执行速度最快。


问题7

问题表述:

关于static变量,下列说法正确的是。

  • 1) 若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度
  • 2) 若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度
  • 3) 设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题
  • 4) 静态全局变量过大,可那会导致堆栈溢出

解析:

静态变量放在程序的全局数据区,而不是在堆栈中分配,所以不可能导致堆栈溢出. 但是如果静态变量的值超过数据类型的表示范围时,此时同样会产生溢出,只是由于静态变量不是存放在堆栈,而是存放在静态存储区,所以产生的溢出不叫堆栈溢出。

(PS : C/C++中,堆栈是内存管理的一种方式,变量存储主要分为三个区域, 栈:局部变量;堆:new/malloc出来的变量;静态存储区: static变量,其中静态变量是在编译期间分配内存)


问题8

问题表述:
对于如下代码,a 和 b 的值分别为多少?

char *p1;int64 *p2;p1=(char *)0x800000;p2=(int64 *)0x800000;char *a=p1+2int64_t *b=p2+2

解析:

0x800002 0x800010

定义指针的时候给指针一个类型就是为了方便指针的加减操作。p1是char类型指针,每个char占一个字节,所以p1+2就是在p1的基础上加2个char的长度,就是两个字节。p2是指向64位int型的指针,所以p2+2就是p2加上两个64位int的长度,也就是加上128位,即16个字节。用16进制表示是0x10。


问题9

问题表述:

以下函数使用正确的是

void test1(){    unsigned char array[MAX_CHAR+1],i;    for(i=0;i<=MAX_CHAR;i++){        array[i]=i;    }}char*test2(){    char p[] = "hello world";    return p;}char *p =test2();void test3(){    char str[10];    str++;    *str='0';}

解析:

  • test1() 重点不在于CHAR_MAX的取值是多少,而是在于i的取值范围是多少。一般char的取值范围是-128到127,而u char 则是0~255,所以i的取值范围是0~255.所以当CHAR_MAX常量大于255时,执行i++后,i不能表示256以上的数字,所以导致无限循环。
  • test2() 重点在于函数中p的身份,他是一个指针,还是数组名;如果是指针p,则p指向存放字符串常量的地址,返回p则是返回字符串常量地址值,调用函数结束字符串常量不会消失(是常量)。所以返回常量的地址不会出错。如果是数组p,则函数会将字符串常量的字符逐个复制到p数组里面,返回p则是返回数组p,但是调用函数结束后p被销毁,里面的元素不存在了。例子中p是数组名,所以会出错,p所指的地址是随机值。若是把char p[]=”hello”;改成char *p=”hello”;就可以了。
  • test3() 重点在于str++;这实际的语句就是str=str+1;而str是数组名,数组名是常量,所以不能给常量赋值。

问题10

问题表述:
以下程序的执行结果是:

class A{    public:        long a;};class B : public A {    public:        long b;};void seta(A* data, int idx) {    data[idx].a = 2;}int main(int argc, char *argv[]) {    B data[4];    for(int i=0; i<4; ++i){        data[i].a = 1;        data[i].b = 1;        seta(data, i);    }    for(int i=0; i<4; ++i){         std::cout << data[i].a << data[i].b;    }    return 0;}

解析: 22221111

因为函数seta()中形参data的类型是A*, A的sizeof是4(32bit系统), 所以对于对A*指针进行加减运算时, 移动的是一个A的长度, 这时该函数中 data[0~3] 指向的是传过来的实参 data[0].a,data[0].b,data[1].a,data[1].b 。如下图:

这里写图片描述

1 0
原创粉丝点击