笔试面试那件小事(常见的C++基础题)
1->简述变量声明和定义的区别
为变量分配地址和存储空间称为定义,不分配地址称为声明。一个变量可以在多个地方声明,但只能在一个地方定义。加入extern修饰的变量的声明,说明此变量将在文件以外或者文件的最后面定义。
2->简述sizeof和strlen的区别
主要区别如下: sizeof是一个操作符,strlen是库函数;sizeof的参数可以是数据的类型,也可以是变量。而strlen的参数只能是以‘\0’结尾的字符串参数。
编译器在编译时就计算出了sizeof的结果,而strlen函数必须在运行时才能计算出来,并且sizeof计算出的是占用内存的大小,strlen计算的是字符串实际的长度
数组做sizeof的参数不会退化,传递给strlen就退化为指针。
3->说说C和C++中的static有什么作用。
在C中static用来修饰局部静态变量和外部静态变量、函数。而C++中除了上述功能外,还用来定义类的成员变量和函数。编程时候最常用到的是static的记忆性。
4->C和C++中动态内存分配的方法和区别
C里面一般使用malloc/free,C++里面可用malloc/free和new/delete.
5->简述C、C++程序编译的内存分配情况
主要在静态存储器,在堆上和栈上分配三种。
6->说说strcpy、sprintf和memcpy三个函数
函数的主要功能:
strcpy:实现字符串变量间的拷贝;sprintf:主要实现其他数据类型格式到字符串格式的转化;Memcpy:主要是内存块间的拷贝
区别:操作对象不同,strcpy的两个操作对象均为字符串,sprintf的操作源对象可以是多种数据类型,目标操作对象只能是字符串;memcpy:两个对象都是任意可以操作的内存地址,并不局限于某种数据类型。 执行效率不同:memcpy最高;strcpy次之;sprintf效率最低。
7->拷贝构造函数和赋值运算符
拷贝构造函数生成新的类对象,而赋值运算符则不能。由于拷贝构造函数是直接构造一个新的类对象,所以在初始化这个对象之前不需要考虑源对象是否和新建对象相同。
8->用递归和非递归两种方法翻转一个链表
typedef Struct ListNode{
Elemtype Data;
ListNode *next;
}ListNode;
Void ReserveList(ListNode *&pHead){ //非递归实现
ListNode *phead=pHead;
ListNode *p=pHead;
ListNode *q=NULL;
while(p!=NULL){
ListNode *pNext=p->next;
p->next=q;
q=p;
p=pNext;
}
pHead=q;
}
ListNode* ReserveList(ListNode *&pHead){ //递归的方式实现
if(!pHead ||!pHead->next)
return pHead;
ListNode *p=ReserveList(pHead->next);
pHead->next->next=pHead;
return pHead;
}
9->说说引用和指针的区别
引用必须被初始化,但是不分配存储空间。指针可以先声明不初始化。在初始化时候需要分配存储空间
引用初始化后不能被改变,指针可以改变所值的对象
不存在指向空值的引用,但是存在指向空值的指针
10->指针常量与常量指针的区别
这是一个比较常见的问题,也就是const char *p和char *const p的差别,前者称为常量指针(指针指向的内容不变),后者是指针常量(指针本身不可再被重新赋值)
const char* p:因为const修饰在*的前面,因此const修饰的是(*p),因此修饰的是p指向的内容为常量
char const * p:这个解释同上
char* const p:const修饰的是变量p,,而变量p的类型是char*.这个意思是p的值为常量
11->数组名和指针的区别
指针是一个变量,有自己对应的存储空,而数组名仅仅是一个符号,不是变量,因此没有自己对应的存储空间。
1,地址相同,大小不同。采用sizeof求值,指针占的大小为4字节,sizeof(数组名)求出数组占的内存大小
2,都可以作为形参,当数组名作为形参的时候退化成指针
12->构造函数是否可以为虚函数,为什么?
构造函数不能是虚函数,而且不能在构造函数中调用虚函数,因为那样实际上执行的是父类对应的函数。可是自己却因此会没构造好。
析构函数可以是虚函数。
13->delete 与delete[]的区别?
简单的来说delete删除一个指针,而delete[]删除的是一个数组。
14->实现一个算法计算一个数的二进制表示方式的1的个数
int Func(x)
{
int Ret=0;
while(x){
Ret++;
x=x&(x-1);
}
return Ret;
}
15->将引用作为函数返回值类型的格式、好处和需要遵守的规则?
格式:类型标识符& 函数名(形参列表){//函数体}
好处:在内存中不产生被返回值的副本(也正是因为如此,所以返回一个局部变量的引用是不可取的,因为随着该局部变量的生存期的结束,相应的引用也会失效)
注意事项:
1:不能返回局部变量的引用。因为局部变量会在函数返回后被销毁,因此被返回的引用就成为了无所指的引用,程序会进入未知状态
2:不能返回函数内部new分配的引用。虽然吧存在局部变量的被动销毁问题,对于这种情况又会面临其他尴尬的局面。例如:被函数返回的引用只是作为一个临时变量出现,而没有被赋值给一个实际的变量,那么这个引用所指的空间就无法be释放。造成内存泄露。
3:可以返回类成员的引用,但最好用const
4:流操作符重载的返回值申明为“引用”的作用:流操作符常常被希望继续使用原来的功能,所以流操作符的返回值必须是支持原来操作的引用
5:在四则运算中不能返回引用,必须构造一个对象作为返回值,考虑可以连续赋值。
16->谈谈对于关联、聚合以及组合的认识
涉及UML中的一些概念:
关联是两个类的一般联系,比如“学生”和“老师”就是一种关联关系
聚合表示has-a的关系,是一种相对松散的关系,聚合不需要对被聚合类负责,用空的菱形表示聚合关系。从实现的角度来看,聚合可以表示为:Class A{,,,,} Class B{A *a,....}
组合表示contains-a的关系,关联性强于聚合:组合类与被组合类有相同的生命周期,组合类要对被组合类负责,采用实心菱形表示组合关系。实现形式:Class A{} Class B{A a}
17->当一个类C没有任何成员变量与成员函数的时候,这时sizeof(C)的值是多少,如果不是0,解释下为什么不是零
一个空类对象的大小是1Byte。这个是被编译器安插进去的一个字节,这样就使得两个空类实例在内存中得以区别。
18->用变量a给出下面的定义
a)一个整型数(int a)
b)一个指向整型数的指针(int * a)
c)一个指向指针的指针,它指向的指针是一个指向一个整型数(int **a)
d)一个有10个整型数的数组(int a[10])
e)一个有10个指针的数组,该指针指向一个整型数(int *a[10])
f)一个指向有10个整型数数组的指针(int (*a)[10])
g)一个指向函数的指针,该函数有一个整型参数并返回一个整型树(int (*a)(int))
h)一个有10个指针的数组,该指针是一个指向函数的指针,函数一个整型形参和一个整型的返回值(int (*a[10])(int))
19->成员函数通过什么来区分不同对象的成员数据?为什么能够区分?
通过this指针来区别,因为它指向的是对象的首地址。
20->拷贝构造函数在哪些情况下会被调用
1)当用类的一个对象去初始化该类的另一个对象时;
2)如果函数的形参是类的对象,调用函数时
3)如果函数的返回值是类的对象,函数调用返回时
21->流运算符为什么不能通过类的成员函数重载?一般怎么解决?
通过类的成员函数重载要求运算符的第一个对象是自己,而流运算符的第一个对象是流对象。一般通过友元函数来解决
22->虚函数与普通成员函数的区别?内联函数和构造函数是否能为虚函数
区别:虚函数以virtual关键字修饰,有虚指针和虚函数表,虚函数一般只有在运行的时候才能确定调用哪个函数。而普通函数则不然。
内联函数和构造函数不能设置为虚函数。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。