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)
- 笔试题集锦(1)
- C、C++笔试题集锦
- C++ 笔试题集锦(1)
- C、C++笔试题、面试题集锦
- iOS的C/C++笔试题集锦
- 《C、C++笔试题集锦+》下载方式
- 单片机笔试题集锦1
- 笔试题集锦(2)
- 笔试题集锦(2)
- java笔试题集锦(一)
- C++ 笔试题集锦(2)
- Java笔试题集锦
- Java笔试题集锦
- 金山笔试题集锦
- Java笔试题集锦
- Java笔试题集锦
- Java笔试题集锦
- android布局的优化方案merge、ViewStub的用法
- 骨骼动画详解-Spine
- 欢迎使用CSDN-markdown编辑器
- 告诉我,这其实是一个悲剧 -_-#
- JS小数点乘法除法问题详解
- C++ 笔试题集锦(1)
- RabbitMQ > 基于MQ的ESB
- 数据结构学习->经验总结1
- 以前写的几个工具性的函数
- java日志组件介绍(common-logging,log4j,slf4j,logback )
- 字符串格式化-String.format()的使用
- java泛型详解
- curosr 查询之查询本地电话
- Activity启动模式相关总结