大家说我这套面试题做得怎么样,考官说我做得一踏糊涂.大家说说!有点郁闷.
1. 简述C++虚拟析构函数的作用。试举例说明。
虚拟析构函数的作用是确保实例化的对象能够调用自己类实现的虚构函数而被完整的虚构(释放).当通过基类的指针或引用去删除派生类的对象,而基类又没有虚析构函数时,结果将是不可确定的,派生类的析构函数永远不会被调用.如:
class base
{
public:
virtual ~base()//注:一个虚拟析构函数
{
cerr << "base destructors" <<endl;
}
};
class dervid: public base
{
public:
~ dervid()
{
cerr << " dervid destructors" << endl;
//释放dervid时其它需要的操作
}
};
int main()
{
base* pb = new dervid ();
//……
delete pb; //此时若基类没的虚构函数不是虚的则派生类的虚构函数(~dervid)则不会//被调用,相应释放dervid时其它需要的操作没有被执行.
//……
return 0;
}
另,当我们不打算以类作为基类时,最好不要使虚拟析构函数因为会为类对象增加一个vptr的内存开销.
2 class Integer
{
public:
Integer(int _n){
cout<<"Construction"<<endl;
n = _n;
};
Integer(Integer& Copy)
{
cout<<"Copy Construction"<<endl;
n = Copy.n;
};
Integer operator = (Integer& Int)
{
cout<< "operator = "<<endl;
n = Int.n;
};
~Integer(){
cout<< "Deconstruction"<<endl;
};
private:
int n;
};
Integer ReturnInteger()
{
Integer Int(2); return Int;
}
void Test()
{
Integer Int(0);
Int = ReturnInteger();
}
请顺序写出执行Test()函数后程序的输出:
Construction //以0为参数构造Int
Construction //以2为参数构造ReturnInteger中的Int
Copy Construction //函数返回时调用拷贝构造函数
Deconstruction //虚构ReturnInteger中以2为参数的Int
operator = //调用operator将_result中n的值赋给Int
Deconstruction //虚构_resul
Deconstruction //虚构以0为参数的Int
//注:编译器会对ReturnInteger()函数作NRV优化
Void ReturnInteger(Integer& _result)
{
Integer Int(2);//调用构造函数产生Int
_result. Integer:: Integer(Int)//以Int为参数调用拷贝构造函数.
return;
}
3. class Base
{
public:
void FuncA(void);
virtual BOOL FuncB(int &x) {x--;}
long m_Data1;
long m_Data2;
};
class DivClass : public Base
{
public:
BOOL FuncB(int &x){ x++;}
virtual FuncC(void);
long m_Data3;
};
void FuncC(void)
{
Base *pBase;
int x = 0;
pBase = new DivClass;
x = sizeof(DivClass);//此时x = 16
pBase-> FuncB(x); //因为FuncB是虚函数,调用DivClass类的FuncB 得x = 17
}
请问x = __17___(32机器)
注:x的大小应该和机器以及编译器有关,32机器与64位机器会有完全不同的结果,以下说明假设为32位操作系统上:基类DivClass的大小是基类的两个long各占4个字节,因为类有虚函数所以会增加一个4个字节的vptr指向相应的vtable,所以基类的大小是12个字节,派生类的大小则需要加上自己的非静态成员long占4个字节,所以派生类的大小为16个字.
另因为在32系统上以4个字节为调整码,但DivClass和Base 的大小都是4的倍数,所以不需在调整.
4 请指出如下程序的问题
#pragma pack(4)
struct DATA_REPORT
{
UC DataLinkNo;
US Length;
UC DataType;
UC DataValue[20];
};
#define DATA_TEST 10
struct DATA_REPORT &FuncA(void)
{
struct DATA_REPORT DataReport;
DataReport.Length = 24;
DataReport.DataType = DATA_TEST;
strcpy( DataReport. DataValue, “FuncA Send Data For Test! Data length is 20”);
return DataReport; //此处返回函数局部对象的引用,因为在函数调用时局部对象将被//收回,所以函数外将得到的只是没有用的信息.
}
BOOL FuncB(struct DATA_REPORT &DataReport)
{
CHAR tmpData[18];//18个字节会导致拷贝出界,但不会出错.
memcpy(tmpData, DataReport.DataValue, DataValue.Length);
return TRUE; //些函数总是返回ture,应该没有什么实际的意义.
}
void FuncC(void)
{
struct DATA_REPORT DataReport;
DataReport=FuncA();//假设FuncA()返回一个地址那么接收的将要是一指针或引用
//所以上面一句应改为struct DATA_REPORT* DataReport;
FuncB(DataReport);
}
5. 实现内存拷贝函数memcpy(void* pdest, const void * psour, int size ) 要求考虑地址重叠的情况
void *memcpy (void * pdest, const void * psour, int size)
{
assert((pdest!= NULL) && (psour!= NULL));
char *pb = (char*) pdest;
char *ps = (char*) psour;
char *temp = new char[size];//由于考虑地址重叠将来源数据暂存起来
char *dt = temp;
for (int i = 0; i < size; ++i) temp[i] = ps [i];//将来源数据拷贝到新申请的内存中
while (i-- > 0)
*( pb ++) = *(temp++);//拷贝到目的地
delete[] dt;//释放分配的地址空间.
return pdest;
}
6. struct List{
struct List *next;
ELEMENT e;
};
struct List *head, *tail;
构造单向链表,然后实现以下函数ReverseList() 将链表反向,即原表头变为表尾,表尾变为表头
typedef int ELEMENT;
struct List{
struct List *next;
ELEMENT ele;
};
class Link
{
struct List *head, *tail;
public:
Link(){ head = tail = NULL; }
~Link(){Erase ();}
int InsertAdd(ELEMENT);//向链表中加入一个值,实现略
int DeleteData(ELEMENT);//在链表中删除指定值的节点,实现略
int DeleteElement(List*);//在链表中删除指定的节点,实现略
int Erase();//删除链表中所有的节点
void reverselist()//反转链表
{
if(head==0)
return;
if(head->next==0)
return;
if(head->next==tail)
{
head->next = 0;
tail->next = head;
}
else
{
List* pre = head;
List* cur = head->next;
List* curnext = cur->next;
head->next = 0;
cur->next = head;
for(; curnext!=0; )
{
cur->next = pre;
pre = cur;
cur = curnext;
curnext = curnext->next;
}
curnext->next = cur;
}
}//end reverselist()
};