1. C++中构造函数和析构函数可以抛出异常吗?
A.都不行
B.都可以
C.只有构造函数可以
D.只有析构函数可以
答案:C
构造函数尽量不抛异常,析构函数不要抛异常。
对于构造函数:
在构造函数中抛出异常,将导致正在构造的对象的析构函数不会被执行。当对象发生部分构造时,已经构造完成的子对象将被逆序的析构(即异常发生点前面的对象),而还没有开始构造的子对象将不会被构造了(即异常发生点后面的对象),但正在构造的子对象和对象自己本身将停止构建,并且它的析构函数树不会被调用的。
对于析构函数:
more effective c++提出两点理由(析构函数不能抛出异常的理由):
(1)如果析构函数抛出异常,则异常点之后的程序不会执行,如果析构函数在异常点之后执行了某些必要动作,比如释放某些资源,则这些动作不会被执行,会造成诸如资源泄露的问题。
(2)通常异常发生时,C++的机制会调用已经构造对象的析构函数来释放资源,此时若析构函数本身也抛出异常,则前一个异常未处理,又出现新的异常会导致程序崩溃。
解决方案:
如果某个操作可能会抛出异常,class应该提供一个普通函数而非析构函数,来执行该操作。
如果无法保证在析构函数不发生异常。我们可以用try catch来将异常吞下。也就是把异常完全封装在析构函数内部,绝不让异常抛出函数之外。
~ClassName()
{
try{
do_something();
}
catch(){
//可以什么都不做,只是保证catch块的程序抛出的异常不会被扔出析构函数之外
}
}
2. 下面四个类A,B,C,D,在32位机器上sizeof(A),sizeof(B),sizeof(C),sizeof(D)值分别为()
class A{
};
class B{
char ch;
int x;
};
class C{
public:
void Print(void){}
};
class D
{
public:
virtual void Print(void){}
};
答案: 1,8,1,4
类A空类型的实例虽然不包含任何信息,但是必须在内存中占一定的空间,否则无法使用这些实例,一般都是1。
类B因为内存对齐所以为8。
类C里面虽然有函数,但是只需要知道函数的地址即可,而这些函数的地址只与类型相关,而
与类型的实例无关,编译器不会因为函数而在内存中多添加任何的额外信息.所以还是1 。
类D因为有虚函数,C++的编译器一旦发现一个类型中有虚函数,就会为该类型生成虚函数表,并在该类型的
每一个实例中添加一个指向虚函数表的指针.因为多了一个指针,所以在32位机器为4,64位机器为8。
3. 假定CSomething是一个类,执行下面这些语句之后,内存里创建了__个CSomething对象。
CSomething a();
CSomething b(2);
CSomething c[3];
CSomething &ra = b;
CSomething d=b;
CSomething *pA = c;
CSomething *p = new CSomething(4);
答案:6
CSomething a();// 没有创建对象,这里不是使用默认构造函数,而是定义了一个函数,在C++ Primer393页中有说明。定义一个函数,参数为空,返回值为CSomething对象, 类似int func() 。
CSomething b(2);//使用一个参数的构造函数,创建了一个对象。
CSomething c[3];//使用无参构造函数,创建了3个对象。
CSomething &ra=b;//ra引用b,没有创建新对象。
CSomething d=b;//使用拷贝构造函数,创建了一个新的对象d。
CSomething *pA = c;//创建指针,指向对象c,没有构造新对象。
CSomething *p = new CSomething(4);//新建一个对象。
4. 有以下程序
#include<iostream>
#include<stdio.h>
using namespace std;
int main(){
int m=0123, n = 123;
printf("%o %o\n", m, n);
return 0;
}
程序运行后的输出结果是()
A.0123 0173
B.0123 173
C.123 173
D.173 173
答案: C
o% 表示8进制进行输出int m=0123; m已经是8进制不需要进行转换 而n=123是10进制 需要进行8进制转换 得173 。
5. 关于虚函数的描述正确的是()
A.派生类的虚函数与基类的虚函数具有不同的参数个数和类型
B.内联函数不能是虚函数
C.派生类必须重新定义基类的虚函数
D.虚函数可以是一个static型的函数
答案:B
内联函数是编译器就展开的,把代码镶嵌就程序,虚函数是动态绑定,运行期决定的。所以内联函数不能是虚函数 。
6. 有以下程序
#include <stdio. h>
int fun( intA )
{
int b = 0;
static int c = 3;
a = ( c + +,b + + );
return ( a );
}
main( )
{
int a = 2,i,k;
for( i = 0;i<2;i + + )
k = fun( a + + );
printf("%d\n",k );
}
程序的输出结果是?
答案:0
这个题目考的是逗号表达式,a = 表达式1,表达式2,则a = 表达式2,同理如果后面有n个表达式的话,返回的是最后一个表达式的值。
7. 有如下程序段:
#include <iostream>
void GetMemeory(char *p)
{
p = (char *)malloc(100);
}
void Test()
{
char *str = NULL;
GetMemeory(str);
strcpy(str, "Thunder");
strcat(str + 2, "Downloader");
printf(str);
}
请问运行Test函数结果是:
A.Thunder Downloader
B.under Downloader
C.Thunderownloader
D.程序崩溃
答案:D
- .参数是值传递,函数给参数p重新开辟空间,所以改变的是p指向的空间,str没变。
- GetMemory函数执行完成后,str仍然指向NULL,所以赋值时回奔溃 。
- 正确的做法应该使用双指针 。
void GetMemory(char **p){
*p = (char *)malloc(100);
}
void Test(){
char *str = NULL;
GetMemory(&str);
printf(str);
}
8. 不能作为重载函数的调用的依据是:
A.参数个数
B.参数类型
C.函数类型
D.函数名称
答案:C
9.以下代码编译有错误,哪个选项能解决编译错误?
class A {
public:
int GetValue() const {
vv = 1;
return vv;
}
private:
int vv;
};
A.改变成员变量”vv”为”mutable int vv”
B.改变成员函数”GetValue”的声明,以使其不是const的
C.都不能修复编译错误
D.都可以修复编译错误
答案:D
mutalbe的中文意思是“可变的,易变的”,跟constant(既 C ++中的const)是反义词。
在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。
我们知道,如果类的成员函数不会改变对象的状态,那么这个成员函数一般会声明成const的。但是,有些时候,我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员就应该被mutalbe来修饰。
10.有一个类B继承自类A,他们数据成员如下:
class A {
...
private:
int &a;
};
class B : public A {
...
private:
int a;
public:
const int b;
A c;
static const char* d;
A* e;
};
则构造函数中,成员变量一定要通过初始化列表来初始化的是__。
答案:b,c
在构造函数中需要初始化列表初始化的有如下三种情况 :
1.带有const修饰的类成员 ,如const int a ;
2.引用成员数据,如 int& p;
3.带有引用的类变量
static成员是不允许在类内初始化的,除了const,那么static const 成员是不是在初始化列表中呢?
当然不是。
一是static属于类,它在未实例化的时候就已经存在了,而构造函数的初始化列表嘞,只有在实例化的时候才执行。
其二是static成员不属于对象。我们在调用构造函数是创建对象,一个跟对象没直接关系的成员要它做什么呢 o(^▽^)o。
11. 类A是类B的友元,类C是类A的公有派生类,忽略特殊情况则下列说法正确的是()
A.类B是类A的友元
B.类C不是类B的友元
C.类C是类B的友元
D.类B不是类A的友元
答案:B,D
友元关系是单向的,不是对称,不能传递。
关于传递性,有人比喻:父亲的朋友不一定是儿子的朋友。
那关于对称性,是不是:他把她当朋友,她却不把他当朋友,(●ˇ∀ˇ●)