Q5:请讲一讲析构函数和虚函数的用法和作用?
A:
析构函数一般在对象撤消前做收尾工作,比如回收内存等工作,虚拟函数的功能是使子类可以用同
析构函数:
如果一个基类的析构函数被说明为虚析构函数,则它的派生类中的析构函数也是虚析构函数,不管它是否使用了关键字virtual进行说明。
不管你是不是建立了自己的析构函数,析构函数总是有的(如果需要的话,编译器会免费送你一个)。那么要不要建立自己的析构函数呢?答案非常简单:
不管你是不是建立了自己的析构函数,析构函数总是有的(如果需要的话,编译器会免费送你一个)。那么要不要建立自己的析构函数呢?答案非常简单:
1、如果类分配了系统资源而没有相应的自动释放机制,需要进行手工资源释放,那么你需要一个析构函数。注意如果有了自动释放机制,例如auto_ptr所管理的内存,那么就不需要析构函数。
2、如果该类必须或者可以作为基类,那么你需要一个虚拟析构函数,即便他什么也不做。这时为了确保删除基类指针时析构派生类对象。
3、除此以外,没理由建立自己的析构函数,即便是一个空析构函数。
析构函数最大的注意事项是不能抛出异常,否则任何使用该类的代码都无法做到异常安全。
虚函数:
虚函数充分体现了面向对象思想中的继承和多态性这两大特性。虚函数是动态联编的基础。虚函数是成员函数,而且是非static的成员函数。 动态联编只能通过指针或引用标识对象来操作虚函数。如果采用一般类型的标识对象来操作虚函数,则将采用静态联编方式调用虚函数。
虚函数的定义要遵循以下重要规则:
1.如果虚函数在基类与派生类中出现,仅仅是名字相同,而形式参数不同,或者是返回类型不同,那么即使加上了virtual关键字,也是不会进行滞后
联编的。
2.只有类的成员函数才能说明为虚函数,因为虚函数仅适合用与有继承关系的类对象,所以普通函数不能说明为虚函数。
3.静态成员函数不能是虚函数,因为静态成员函数的特点是不受限制于某个对象。
4.内联(inline)函数不能是虚函数,因为内联函数不能在运行中动态确定位置。即使虚函数在类的内部定义定义,但是在编译的时候系统仍然将它看做是非内联的。
5.构造函数不能是虚函数,因为构造的时候,对象还是一片位定型的空间,只有构造完成后,对象才是具体类的实例。
6.析构函数可以是虚函数,而且通常声名为虚函数。
1.找错
Void test1()
{
char string[10];
char* str1="0123456789";
strcpy(string, str1);
}
Void test2()
{
char string[10], str1[10];
for(I=0; I<10;I++)
{
str1[i] ='a';
}
strcpy(string, str1);
}
Void test3(char* str1)
{
char string[10];
if(strlen(str1)<=10)
{
strcpy(string, str1);
}
}
答:test1数组长度不够,test2的for有问题,test3的strlen恒等于2。
2. 找错
#define MAX_SRM 256
DSN get_SRM_no()
{
static int SRM_no;
int I;
for(I=0;I<MAX_SRM;I++,SRM_no++)
{
SRM_no %= MAX_SRM;
if(MY_SRM.state==IDLE)
{
break;
}
}
if(I>=MAX_SRM)
return (NULL_SRM);
else
return SRM_no;
}
答:出这题目的人够无聊的,看我半天,这种垃圾题拿去烧了。随便找了下,大家有兴趣自己再找,记得通知我。
If(my_.......)==IDLE 改成 IDLE==if(….),良好的编程风格。
3. 写出程序运行结果
int sum(int a)
{
auto int c=0;
static int b=3;
c+=1;
b+=2;
return(a+b+c);
}
void main()
{
int I;
int a=2;
for(I=0;I<5;I++)
{
printf("%d,", sum(a));
}
}
答:运行结果:8,10,12,14,16。因为静态变量。
4. 写出结果
int func(int a)
{
int b;
switch(a)
{
case 1: b=30;
case 2: b=20;
case 3: b=16;
default: b=0
}
return b;
}
则func(1)=?
答:0
5:写出结果
int a[3];
a[0]=0; a[1]=1; a[2]=2;
int *p, *q;
p=a;
q=&a[2];
则a[q-p]=?
答:2
6. 定义 int **a[3][4], 则变量占有的内存空间为:__4___
7. 编写一个函数,要求输入年月日时分秒,输出该年月日时分秒的下一秒。如输入 2004 年 12 月 31 日 23时59分59秒,则输出 2005 年 1 月 1 日 0时0分0秒。
答:
#include<stdio.h>
#include<iostream.h>
#include<string.h>
typedef struct {
int tm_year;
short tm_second;
short tm_minute;
short tm_hour;
short tm_day;
short tm_month;
} time_all;
bool isRun(int year)//判断是否为闰年
{
if(year%4==0)
{
if(year%100==0)
{
if(year%400==0) return true;
else return false;
}
else return true;
}
else return false;
}
int getDays(int month,int year)//根据月份和年份算出当月的天数
{
int a[12]={31,28,31,30,31,30,31,30,31,30,30,31};
if(month!=2) return a[month-1];
else
{
if(isRun(year)) return 29;
else return 28;
}
}
int addSecond(time_all& nowTime)//增加一秒
{
// time_all tempStruct;
// memcpy(&tempStruct,&nowTime,sizeof(time_all));
if((nowTime.tm_second=++nowTime.tm_second%60)!=0)
return 0;
if((nowTime.tm_minute=(nowTime.tm_minute+1)%60)!=0)
return 0;
if((nowTime.tm_hour=(nowTime.tm_hour+1)%24)!=0)
return 0;
if(nowTime.tm_day!=getDays(nowTime.tm_month,nowTime.tm_year))
{
nowTime.tm_day++;
return 0;
}
else nowTime.tm_day=1;
if(nowTime.tm_month!=12)
{
nowTime.tm_month++;
return 0;
}
else nowTime.tm_month=1;
nowTime.tm_year++;
return 0;
}
void main()
{
time_all timeNow;
cout<<"输入时间 依次顺序为年、月、日、时、分、妙"<<endl;
cin>>timeNow.tm_year>>timeNow.tm_month>>timeNow.tm_day>>timeNow.tm_hour>>timeNow.tm_minute>>timeNow.tm_second;
addSecond(timeNow);
cout<<timeNow.tm_year<<"年"<<timeNow.tm_month<<"月"<<timeNow.tm_day<<"日"<<timeNow.tm_hour<<"时"<<timeNow.tm_minute<<"分"<<timeNow.tm_second<<"秒";
}
8.子函数参数压站次序。
答:函数参数入栈的方式
在函数调用过程中,会使用堆栈,这三个表示不同的堆栈调用方式和释放方式。
比如说__cdecl,它是标准的c方法的堆栈调用方式,就是在函数调用时的参数压入堆栈是与函数的声明顺序相反的,其它两个可以看MSDN,不过这个对我们编程没有太大的作用
调用约定
调用约定(Calling convention)决定以下内容:函数参数的压栈顺序,由调用者还是被调用者把参数弹出栈,以及产生函数修饰名的方法。MFC支持以下调用约定:
_cdecl
按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于“C”函数或者变量,修饰名是在函数名前加下划线。对于“C++”函数,有所不同。
如函数void test(void)的修饰名是_test;对于不属于一个类的“C++”全局函数,修饰名是?test@@ZAXXZ。
这是MFC缺省调用约定。由于是调用者负责把参数弹出栈,所以可以给函数定义个数不定的参数,如printf函数。
_stdcall
按从右至左的顺序压参数入栈,由被调用者把参数弹出栈。对于“C”函数或者变量,修饰名以下划线为前缀,然后是函数名,然后是符号“@”及参数的字节数,如函数int func(int a, double b)的修饰名是_func@12。对于“C++”函数,则有所不同。
所有的Win32 API函数都遵循该约定。
_fastcall
头两个DWORD类型或者占更少字节的参数被放入ECX和EDX寄存器,其他剩下的参数按从右到左的顺序压入栈。由被调用者把参数弹出栈,对于“C”函数或者变量,修饰名以“@”为前缀,然后是函数名,接着是符号“@”及参数的字节数,如函数int func(int a, double b)的修饰名是@func@12。对于“C++”函数,有所不同。
未来的编译器可能使用不同的寄存器来存放参数。
thiscall
仅仅应用于“C++”成员函数。this指针存放于CX寄存器,参数从右到左压栈。thiscall不是关键词,因此不能被程序员指定。
naked call
采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked call不产生这样的代码。
naked call不是类型修饰符,故必须和_declspec共同使用,如下:
__declspec( naked ) int func( formal_parameters )
{
// Function body
}
过时的调用约定
原来的一些调用约定可以不再使用。它们被定义成调用约定_stdcall或者_cdecl。例如:
#define CALLBACK __stdcall
#define WINAPI __stdcall
#define WINAPIV __cdecl
#define APIENTRY WINAPI
#define APIPRIVATE __stdcall
#define PASCAL __stdcall
c/c++的函数参数压栈顺序.
为了这句话丢了很多次人.无所谓了,反正咱脸皮厚.
总结一下: 编译出来的c/c++程序的参数压栈顺序只和编译器相关!
下面列举了一些常见的编译器的调用约定
VC6:
调用约定 堆栈清除 参数传递
__cdecl 调用者 从右到左,通过堆栈传递
__stdcall 函数体 从右到左,通过堆栈传递
__fastcall 函数体 从右到左,优先使用寄存器(ECX,EDX),然后使用堆栈
thiscall 函数体 this指针默认通过ECX传递,其它参数从右到左入栈
__cdecl是C/C++的默认调用约定; VC的调用约定中并没有thiscall这个关键字,它是类成员函数默认调用约定;
C/C++中的main(或wmain)函数的调用约定必须是__cdecl,不允许更改;
默认调用约定一般能够通过编译器设置进行更改,如果你的代码依赖于调用约定,请明确指出需要使用的调用约定;
C++Builder6:
调用约定 堆栈清除 参数传递
__fastcall 函数体 从左到右,优先使用寄存器(EAX,EDX,ECX),然后使用堆栈 (兼容Delphi的register)
(register与__fastcall等同)
__pascal 函数体 从左到右,通过堆栈传递
__cdecl 调用者 从右到左,通过堆栈传递(与C/C++默认调用约定兼容)
__stdcall 函数体 从右到左,通过堆栈传递(与VC中的__stdcall兼容)
__msfastcall 函数体 从右到左,优先使用寄存器(ECX,EDX),然后使用堆栈(兼容VC的__fastcall)
上述资料来源于HouSisong的一篇文章:http://www.allaboutprogram.com/index.php?option=content&task=view&id=29&Itemid=31.
由于能力和资源有限,只能找到这些东西,主要的差异体现在fastcall上面,vc是前两个参数放入寄存器,后面的压栈,bcb是前3哥参数使用寄存器,
更有变态的,一个朋友朋友说有的参数超过7个以后前5个从左到右传递,后面的从右到走,上面说的不可不信,不可全信.
如何确定你的编译采用的那种顺序那?
#include <stdio.h>
int f(int i,int j,int k);
int main()
{
static int i=0;
f(i++,i++,i++);
return 0;
}
int f(int i,int j,int k)
{
int l;
int g;
printf("k=%d:[%x]/n",k,&k);
printf("j=%d:[%x]/n",j,&j);
printf("i=%d:[%x]/n",i,&i);
printf("___________/n");
printf("l:%x/n",&l);
printf("g:%x/n",&g);
}
看看k->i的地址的增长顺序和l->g的顺序是否相同,如果相同则是从右到左,否则从左到右.
PS:
本来通过打印参数的值来判断那个先入栈,结果被一个朋友批评了,
他说:压栈顺序和参数计算顺序不是一回事,所以还是看地址更有保证.
//看过的朋友当作笑谈吧。
曾经看到一篇文章上面说:c/c++参数压栈顺序是从右到左,pascal参数压栈是从左到右.
9.子函数参数出栈如何完成?
答:这个不懂,也找不到。知道的朋友介绍一下。
1、1000!有几位数,为什么
答:
运用取以10为底的对数,有对于任意的非负十进制数n,
对其取以10为底的对数函数lgn的结果向下取整后加1表示该数的十进制位数,
举例如下(方括号“〔〕”表示对其中的数进行向下取整):
当n=1时,lg1=0,〔lg1〕+1=0+1=1,n为1位十进制数;
当n=2时,lg2=0.30,〔lg2〕+1=0+1=1,n为1位十进制数;
… …
当n=10时,lg10=1,〔lg10〕+1=1+1=2,n为2位十进制数;
当n=11时,lg11=1.041,〔lg11〕+1=1+1=2,n为2位十进制数;
… …
当n=100时,lg100=2,〔lg100〕+1=2+1=3,n为3位十进制数;
… …
当n=468时,lg468=2.670,〔lg468〕+1=2+1=3,n为3位十进制数;
… …
当n=1000时,lg1000=3,〔lg1000〕+1=3+1=4,n为4位十进制数;
所以,当n=1000!时,
[lg(1000!)]+1
=〔lg(1*2*3*…*998*999*1000)〕+1
=[lg1+lg2+lg3+...+lg998+lg999+lg1000]+1
=2567+1
=2568
即,1000!有2568位。
2、
F(n)=1 n>8 n<
12
F(n)=2 n<
2
F(n)=3 n=
6
F(n)=4 n=other
使用+ - * /和sign(n)函数组合出F(n)函数
sign(n)=0 n=0
sign(n)=-1 n<0
sign(n)=1 n>0
答:
3、编一个程序求质数的和例如F(7)=1+3+5+7+11+13+17=58
解:
#include <iostream>
#include "math"
///判断是不是素数
bool IsPrime(int Num)
{
int i;
if (Num < 2)
return false;
for (i=2; i<=sqrt(Num); i++)
{
if (Num%i == 0)
break;
}
}
void main()
{
int low, up;
int total=0;
do{
cout<<"输入下限: ";
cin>>low;
cout<<"输入上限: ";
cin>>up;
}while(low>=up);
for(int t=low; t<=up; t++)
if( IsPrime(t) )
total+=t;
cout<<total<<"/t";
cout<<endl;
}
1. 三个
(a+b)+c==(b+a)+c
(a+b)+c==(a+c)+bfloat:a,b,c 问值
答:有可能是00,有可能是11。因为float没0值。
2. 把一个链表反向填空
答:
3. 设计一个重采样系统,说明如何anti-alias
答:
4. y1(n)=x(2n), y2(n)=x(n/2),问:
如果y1为周期函数,那么x是否为周期函数//是
如果x为周期函数,那么y1是否为周期函数//是
如果y2为周期函数,那么x是否为周期函数//是
如果x为周期函数,那么y2是否为周期函数//是
5. 如果模拟信号的带宽为5KHZ,要用8K的采样率,怎么办。
答:
4. 某个程序在一个嵌入式系统(
200M
的CPU,
50M
的SDRAM)中已经最化了,换到另一个系统
(
300M
的CPU,
50M
的SDRAM)中运行,还需要优化吗?
5. x^4+a*x^3+x^2+c*x+d最少需要作几次乘法
6. 什么情况下,sin(x+y)+y ~ ....
7. 下面哪种排序法对12354最快
a quick sort
b.buble sort
c.merge sort
8. 哪种结构,平均来讲,获取一个值最快
a. binary tree
b. hash table
c. stack
问:有13个人 围圈坐 从1开始数数 逢个位数为3退 问最后一个原来的序号 编程实现 (典型的约瑟夫问题 )
//header.h
#ifndef N
#define N
typedef struct link
{
int data;
struct link *next;
}lk;
void create(lk *const&,int k);
void out(lk *const&);
#endif
//func.cpp
#include <iostream>
#include "header.h"
using namespace std;
void create(lk *const &head,int k)
{
lk *tem,*p;
tem=p=head;
int i;
for(i=1;i<k;++i)
{
p->data=i;
p->next=new lk;
p=p->next;
}
p->data=i;
p->next=head;
}
void out(lk *const &head)
{
lk *p=head,*tem=NULL;
int i=1;
while(p->next!=p)
{
if(!(i%3))
{
cout<<tem->next->data<<endl;
system("pause");
tem->next=tem->next->next;
delete p;
p=tem;
/*tem=tem->next;
delete tem;*/
}
++i;
tem=p;
p=p->next;
}
cout<<p->data;
}
//main.cpp
#include <iostream>
#include "header.h"
using namespace std;
int main()
{
lk *head=new lk,*tem;
head->next=NULL;
int k;
cout<<"你想让你的环有几个结点:";
cin>>k;
create(head,k);
out(head);
system("pause");
return 0;
}