第一题:以下程序的输出是___________________
class Base
{
public:
Base(int j):i(j){}
virtual ~Base(){}
void func1(){i *= 10; func2();}
int getValue(){return i;}
protected:
virtual void func2(){i++;}
protected:
int i;
};
class Child:public Base
{
public:
Child(int j):Base(j){}
void func1(){i *= 100; func2();}
protected:
void func2(){i += 2;}
};
int main()
{
Base* pb = new Child(1);
pb->func1();
cout<<pb->getValue()<<endl;
delete pb;
return 0;
}
此题输出:12.(在非虚函数中调用虚函数,将调用实际的虚函数)
第三题:以下程序的输出是___________________
int main()
{
char num;
for(num = 0;num < 255;)
num += num;
cout<<num<<endl;
return 0;
}
此题是死循环,无输出。(for循环中将num赋值为0了,在for循环上面是一个随机的值。所以num+num还是0,此题说明无论在什么情况下一定要坚持自己的判断)。
第四题:程序出错在什么阶段:
#include
using namespace std;
int main()
{
http://www.linuxidc.com
cout<<“welcome to linuxidc”<<endl;
return 0;
}
A:编译时;B:运行时;C:编译和运行时都出错;D程序运行正常
选D。因为http://www.linuxidc.com中//后面是注释,前面是标签(类似goto的标签)。(坑爹啊)
第五题:下面程序的执行结果是【说明:x86_64环境】(*间接访问的优先级大于-)
int a[4][4] = {
{1,2,3,4},
{50,60,70,80},
{900,1000,1100,1200},
{13000,14000,15000,16000}
};
int (*p1)[4] = a;
int (*p2)[4] = &a[0];
int *p3 = &a[0][0];
cout<<*(*(a + 1) – 1)<<” “<<*(*(p1 + 3) – 2) + 1
<<” “<<*(*(p2 – 1) + 16) + 2<<” “
<<*(p3 + sizeof(p1) – 3)<<endl;
输出结果为:4 1101 13002 2。
p1为指向一维数组的指针,所以a + 1指向{50,60,70,80}这一维的地址。减一则为4的地址;同理第二个输出1101。同理,由于数组的列是4,所以*(p2 – 1) + 16就相当于*(p2) + 12,所以第三个输出13002。
第四个由于p1是指针,所以sizeof(p1)为4,所以第四个输出2。
第六题:在32位操作系统gcc编译环境下,下面程序的运行结果为:
class A
{
public:
int b;
char c;
virtual void print()
{cout<<“this is a father’s function!”<<endl;}
};
class B :public A
{
public:
virtual void print()
{cout<<“this is a children’s function!”<<endl;}
};
int main()
{
cout<<sizeof(A)<<” “<<sizeof(B)<<endl;
return 0;
}
输出为12,12.注意:子类继承父类的虚表并覆盖其中的条目。而且指向虚表的指针在每个类中也只有一个。
注意下面的情况(有两个虚表指针):
class A
{
public:
void foo(){}
};
class B:virtual public A
{
public:
void foo(){}
};
class C:virtual public A
{
public:
void foo(){}
};
class D: public B,public C
{
public:
void foo(){}
};
int main(int argc, char *argv[])
{
cout<<“A 的大小为: “<<sizeof(A)<<endl;
cout<<“B 的大小为: “<<sizeof(B)<<endl;
cout<<“C 的大小为: “<<sizeof(C)<<endl;
cout<<“D 的大小为: “<<sizeof(D)<<endl;
return 0;
}
输出1,4,4,8。前三个不解释。从第四个的输出可以看出,虚继承有两个虚表指针。
第八题:关于C++标准模板库,下列说法错误的有哪些:
A std::auto_ptr类型的对象,可以放到std::vector >容器中;
B std::shared_ptr类型的对象,可以放到std::vector >容器中;
C 对于复杂类型T的对象tObj,++tObj和tObj++的执行效率相比,前者更高
D 采用new操作符创建对象时,如果没有足够的内存空间而导致创建失败,则new操作符会返回NULL。
选择:A,D。A中auto是给别人东西而自己没有了。所以不符合vector的要求。而B可以。C不解释。D选项在谭浩强的C++程序设计上说是正确的(谭浩强写书时可能是正确的,那时C++的新标准还没出来),但是实际上市错误的。因为:new操作符有三种,(1)plain new/delete就是operator new/delete。这种在为标准化时,C++中的new运算符总是返回NULL表示分配失败,就像malloc一样,因此程序员不得不检查其返回值。标准C++修订了new的语义,plain new在失败后抛出标准异常std::bad_alloc而不是返回NULL。然而,现在很多使用C++的程序员还以为new是以前的老样子,于是通过检查其返回值是否为NULL来判断分配成功与否,显然这是徒劳的;(2) nothrow new就是不抛出异常的运算符new的形式,nothrow new在失败时返回NULL,所以使用它就不需要设置异常处理器。而是像过去一样检查返回值是否为NULL即可。(3)placement new/delete:在指定的内存地址调用构造函数和析构函数,不会引起内存的分配。
PS:auto_ptr和shared_ptr的定义方法: auto_ptr<对象> pInv(对象的指针);shared_ptr<对象> pInv(对象的指针)。如果要得到对象的指针,使用pInv.get()成员函数。
第十题:在Inter CPU上,以下多线程对int型变量x的操作,哪几个不是原子操作,假定变量的地址都是对齐的。
A x = y B x++ C ++x D x=1
选ABC。看下在VC++6.0下的汇编命令即可:从图可以看出本题只有D选项才是原子操作。
在VC++6.0下看汇编命令的方法:
在VC6.0编译器对g_nLoginCount++()(例子而已);这一语句打个断点,再按F5进入调试状态,然后按下Debug工具栏的Disassembly按钮,这样就出现了汇编代码窗口。可以发现在C/C++语言中一条简单的自增语句其实是由三条汇编代码组成的,如下图所示。
第十一题:
class A
{
string a;
void f1(){printf(“Hello World”);}
void f2(){a = “Hello World”; printf(“%s”,a.c_str());}
virtual void f3(){printf(“Hello World”);}
virtual void f4(){a = “Hello World”;printf(“%s”,a.c_str());}
public:
static int i;
static void f5(){cout<<i<<endl;}
};
一般情况下,下面哪些操作会执行失败?
A A* aptr = NULL;aptr->f1();
B A* aptr = NULL;aptr->f2();
C A* aptr = NULL;aptr->f3();
D A* aptr = NULL;aptr->f4();
选:A,B,C,D。我选的B,C, D。至于A为什么正确,因为A没有使用任何成员变量,而成员函数是不属于对象的,所以A正确。其实,A* aptr = NULL;aptr->f5();也是正确的,因为静态成员也是不属于任何对象的。至于BCD,在B中使用了成员变量,而成员变量只能存在于对象,C有虚表指针,所以也只存在于对象中。D就更是一样了。但是,坑爹啊,在Class A中没有写public,所以全是private,以至于选项中所以的都将失败。真是一个坑加一个坑啊。
第十二题:下列哪些template实例化使用,会引起编译错误?
template
class stack;
void fi(stack); //A
class Ex
{
stack& rs; //B
stack si; //C
};
int main()
{
stack* sc; //D
fi(*sc); //E
int i = sizeof(stack); //F
return 0;
}
选C E F; 请注意stack和fi都只是声明不是定义。我还以为在此处申明后,会在其他地方定义呢,坑爹啊。
由于stack只是声明,所以C是错误的,stack不能定义对象。E也是一样,stack只是申明,所以不能执行拷贝构造函数,至于F,由于stack只是声明,不知道stack的大小,所以错误。如果stack定义了,将全是正确的。
第九题:
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* pb = static_cast<B*>(static_cast<C*>(pa));
}
A bar1无法通过编译 B bar2无法通过编译
C bar3无法通过编译 D bar1可以正确运行,但是采用了错误的cast方法
选B。dynamic_cast是在运行时遍历继承树,所以,在编译时不会报错。但是因为A和B没啥关系,所以运行时报错(所以A和D都是错误的)。static_cast:编译器隐式执行的任何类型转换都可由它显示完成。其中对于:(1)基本类型。如可以将int转换为double(编译器会执行隐式转换),但是不能将int*用它转换到double*(没有此隐式转换)。(2)对于用户自定义类型,如果两个类无关,则会出错(所以B正确),如果存在继承关系,则可以在基类和派生类之间进行任何转型,在编译期间不会出错。所以bar3可以通过编译(C选项是错误的)。
第三十二题(MFC):
int func(){char b[2] = {0};strcpy(b,”aaaa”);}
以下说法哪个正确:
A Debug版崩溃,Release版正常
B Debug版正常,Release版崩溃
C Debug版崩溃,Release版崩溃
D Debug版正常,Release版正常
选A。因为在Debug中有ASSERT断言保护,所以要崩溃,而在Release中就会删掉ASSERT,所以会出现正常运行。但是不推荐如此做,因为这样会覆盖不属于自己的内存,这是搭上了程序崩溃的列车。以上的原因只是猜测,如果读者觉得还有更好的原因可以解释,请回复,谢谢。