C++常见面试题
1.定义一个空类型,里面没有任何成员变量和成员函数。对该类型求sizeof,得到的结果?如果在该类型中添加一个构造函数和析构函数,在对该类型求sizeof,得到的结果又是多少?如果把析构函数标记为虚函数呢?
结果为1,为什么不是0,空类型的实例中不包含任何信息,本来求sizeof应该是0,但是当我们声明类型的实例的时候,它必须在内存中占有一定的空间,否则无法使用这些实例。至于占多少内存,由编译器决定。在VS中每个空类型的实例大小占有一个字节的空间。添加构造函数和析构函数后,该类型的大小还是1,因为调用构造函数和析构函数只需要知道函数的地址即可,而这些函数的地址只与类型有关,而与类型的实例无关,编译器不会因为这两个函数而在实例中添加任何额外的信息。C++编译器一旦发现一个类型中有虚函数,就会为该类型生成一个虚函数表,并在类型的每一个实例中添加一个指向虚函数表的指针。在32位机器上,一个指针占4字节。在64位机器上,占8字节。
2.编译运行下图中的C++代码,结果是什么?(A)编译错误;(B)编译成功,运行时程序崩溃;(C)编译运行正常,输出10。请选择正确答案并分析原因。此题换一个问法就是:“拷贝构造函数的参数为什么必须使用引用类型?”这个问题, 你会怎么回答? 或许你会回答为了减少一次内存拷贝?
class A
{
private:
int value;
public:
A(int n)
{
value = n;
}
A(A other)
{
value = other.value;
}
void Print()
{
cout<<value<<endl;
}
};
int main(void)
{
A a = 10;
A b = a;
b.Print();
return 0;
}
答案是A,在上面的例子中,拷贝构造函数A(A other)传入的参数就是A的一个实例,由于是传值参数,我们把形参拷贝给实参会调用拷贝构造函数。因为如果允许拷贝构造函数传值,就会在拷贝构造函数内调用拷贝构造函数,就会形成永无休止的递归调用从而导致溢出。要解决这个问题可以将构造函数修改位为A(const A& other),也就是把传值参数该位常量引用。说明拷贝构造函数的参数使用引用类型不是为了减少一次内存拷贝, 而是避免拷贝构造函数无限制的递归下去。
3.数组与指针的联系与区别
#include<iostream>
using namespace std;
int getsize(int data[])
{
return sizeof(data);
}
int main()
{
int data1[] = { 1,2,3,4,5 };
int size1 = sizeof(data1);
int *data2 = data1;
int size2 = sizeof(data2);
int size3 = getsize(data1);
cout << size1 <<" "<< size2<<" " << size3 << endl;
}
输出:20 4 4
当声明一个数组时,数组的名字也是一个指针,该指针指向数组第一个元素,data1是数组,sizeof(data1)表示数组大小,但data2是指针,sizeof(data2)表示指针大小,为4。在C/C++中数组作为函数的参数传递时,数组会自动退化为同类型的指针,尽管函数getsize的参数为数组,但它会退化为指针,因为size3为4.
4.字符串
注意C中字符串数组和指针的区别
int main() {
char str1[] = "hallo world";
char str2[] = "hallo world";
//str1地址和str2的地址不同,分别分配空间,然后复制"hallo world"
char* str3 = "hallo world";
char* str4 = "hallo world";
//str3地址和str4地址相同,指向同一块内存(字符串常量)
}