protected: private: };int human::human_num = 0; human f1(human x) { x.print(); return x; }
int main(int argc, char* argv[]) {
human h1; h1.print();
human h2 = f1(h1); h2.print(); return 0; } 输出: 1 1 0 0 -1 -2
---------------------------- 分析:
human h1; //调用构造函数,—hum_num = 1; h1.print(); //输出:‖human is 1″
human h2 = f1(h1); //再调用f1(h1)的过程中,由于函数参数是按值传递对象,调用默认的复制构造函数,它并没有对hum_num++,所以hum_num 仍= 1,所以x.print()输出:‖human is 1″; 在推出f1函数时,要销毁X,调用析构函数(human_num–),输出:‖human is 0″(,由于该函数返回一个human 对象,所以又调用默认构造函数,创建一个临时对象(human_num = 0;),把临时对象赋给h2,又调用默认构造函数( human_num = 0); h2.print(); //输出: human is 0; //在退出main()函数是,先销毁h2,调用析构函数(human_num–),输出‖human_num is -1″ 然后销毁h1,调用析构函数(–),输出‖human_num is -2″ 1.3 Windows的消息机制 1. 的消息机制1Windows
Windows是一个消息(Message)驱动系统。Windows的消息提供了应用程序之间、应用程序与Windows系统之间进行通信的手段。应用程序想要实现的功能由消息来触发,并且靠对消息的响应和处理来完成。
Windows系统中有两种消息队列:系统消息队列和应用程序消息队列。计算机的所有输入设备由Windows监控。当一个事件发生时,Windows先将输入的消息放入系统消息队列中,再将消息拷贝到相应的应用程序消息队列中。应用程序的消息处理程序将反复检测消息队列,并把检测到的每个消息发送到相应的窗口函数中。这便是一个事件从发生至到达窗口函数必须经历的过程。
必须注意的是,消息并非是抢占性的,无论事件的缓急,总是按照到达的先后派对,依次处理(一些系统消息除外),这样可能使一些实时外部事件得不到及时处理。 2. 的消息机制2Windows
Windows中的消息是放在对应的进程的消息队列里的。可以通过GetMessage取得,并且对于一般的消息,此函数返回非零值,但是对于WM_QUIT消息,返回零。可以通过这个
特征,结束程序。当取得消息之后,应该先转换消息,再分发消息。所谓转换,就是把键盘码的转换,所谓分发,就是把消息分发给对应的窗口,由对应的窗口处理消息,这样对应窗体的消息处理函数就会被调用。两个函数可以实现这两个功能:TranslateMessage和DispatchMessage。
另外,需要注意,当我们点击窗口的关闭按钮关闭窗口时,程序并没有自动退出,而是向程序发送了一个WM_DESTROY消息(其实过程是这样的,首先向程序发送WM_CLOSE消息,默认的处理程序是调用DestroyWindow销毁窗体,从而引发WM_DESTROY消息),此时在窗体中我们要响应这个消息,如果需要退出程序,那么就要向程序发送WM_QUIT消息(通过PostQuitMessage实现)。一个窗体如果想要调用自己的消息处理函数,可以使用SendMessage向自己发消息。
如上所述,大部分(注意是大部分)的消息是这样传递的:首先放到进程的消息队列中,之后由GetMessage取出,转换后,分发给对应的窗口。这种消息成为存储式消息。存储式消息基本上是使用者输入的结果,以击键(如WM_KEYDOWN和WM_KEYUP讯息)、击键产生的字符(WM_CHAR)、鼠标移动(WM_MOUSEMOVE)和鼠标按钮
(WM_LBUTTONDOWN)的形式给出。存储式消息还包含时钟消息(WM_TIMER)、更新消息(WM_PAINT)和退出消息(WM_QUIT)。但是也有的消息是直接发送给窗口的,它们被称为非存储式消息。例如,当WinMain调用CreateWindow时,Windows将建立窗口并在处理中给窗口消息处理函数发送一个WM_CREATE消息。当WinMain调用ShowWindow时,Windows将给窗口消息处理函数发送WM_SIZE和
WM_SHOWWINDOW消息。当WinMain调用UpdateWindow时,Windows将给窗口消息处理函数发送WM_PAINT消息。 2 网络知识 2.1 OSI和TCP/IP
1. 的七层网络结构图(功能及特点)OSI
1) 物理层:为数据链路层提供物理连接,在其上串行传送比特流,即所传送数据的单位是比特。此外,该层中还具有确定连接设备的电气特性和物理特性等功能。
2) 数据链路层:负责在网络节点间的线路上通过检测、流量控制和重发等手段,无差错地传送以帧为单位的数据。为做到这一点,在每一帧中必须同时带有同步、地址、差错控制及流量控制等控制信息。
3) 网络层:为了将数据分组从源(源端系统)送到目的地(目标端系统),网络层的任务就是选择合适的路由和交换节点,使源的传输层传下来的分组信息能够正确无误地按照地址找到目的地,并交付给相应的传输层,即完成网络的寻址功能。
4) 传输层:传输层是高低层之间衔接的接口层。数据传输的单位是报文,当报文较长时将它分割成若干分组,然后交给网络层进行传输。传输层是计算机网络协议分层中的最关键一层,该层以上各层将不再管理信息传输问题。
5) 会话层:该层对传输的报文提供同步管理服务。在两个不同系统的互相通信的应用进程之间建立、组织和协调交互。例如,确定是双工还是半双工工作。
6) 表示层:该层的主要任务是把所传送的数据的抽象语法变换为传送语法,即把不同计算机内部的不同表示形式转换成网络通信中的标准表示形式。此外,对传送的数据加密(或解密)、正文压缩(或还原)也是表示层的任务。
7) 应用层:该层直接面向用户,是OSI中的最高层。它的主要任务是为用户提供应用的接口,即提供不同计算机间的文件传送、访问与管理,电子邮件的内容处理,不同计算机通过网络交互访问的虚拟终端功能等。 2. (功能及特点)TCP/IP
1) 网络接口层:这是TCP/IP协议的最低一层,包括有多种逻辑链路控制和媒体访问协议。网络接口层的功能是接收IP数据报并通过特定的网络进行传输,或从网络上接收物理帧,抽取出IP数据报并转交给网际层。
2) 网际网层(IP层):该层包括以下协议:IP(网际协议)、ICMP(Internet Control Message Protocol,因特网控制报文协议)、ARP(Address Resolution Protocol,地址解析协议)、RARP(Reverse Address Resolution Protocol,反向地址解析协议)。该层负责相同或不同网络中计算机之间的通信,主要处理数据报和路由。在IP层中,ARP协议用于将IP地址转换成物理地址,RARP协议用于将物理地址转换成IP地址,ICMP协议用于报告差错和传送控制信息。IP协议在TCP/IP协议组中处于核心地位。
3) 传输层:该层提供TCP(传输控制协议)和UDP(User Datagram Protocol,用户数据报协议)两个协议,它们都建立在IP协议的基础上,其中TCP提供可靠的面向连接服务,UDP提供简单的无连接服务。传输层提供端到端,即应用程序之间的通信,主要功能是数据格式化、数据确认和丢失重传等。
4) 应用层:TCP/IP协议的应用层相当于OSI模型的会话层、表示层和应用层,它向用户提供一组常用的应用层协议,其中包括:Telnet、SMTP、DNS等。此外,在应用层中还包含有用户应用程序,它们均是建立在TCP/IP协议组之上的专用程序。 3. 参考模型和TCP/IP参考模型的区别:OSI 1) OSI模型有7层,TCP/IP只有4层;
2) OSI先于协议出现,因此不会偏向于任何一组特定的协议,通用性更强,但有些功能不知该放哪一层上,因此不得不加入一些子层;TCP/IP后于协议出现,仅是将已有协议的一个描述,因此两者配合的非常好;但他不适合其他的协议栈,不容易描述其他非TCP/IP的网络;
3) OSI中网络层同时支持无连接和面向连接的通信,但在传输层上只支持面向连接的通信;TCP/IP中网络层只支持无连接通信,传输层同时支持两种通信; 4) 在技术发生变化时,OSI模型比TCP/IP模型中的协议更容易被替换。
4. 请你详细的解释一下IP协议的定义,在哪个层上面,主要有什么作用? TCP与UDP呢?
解:与IP协议配套使用的还有三个协议: ARP-地址解析协议 RARP-逆地址解析协议
ICMP-因特网控制报文协议ICMP IP协议-网际协议 IP地址、IP包头 2.2 交换机和路由器
1. 请问交换机和路由器分别的实现原理是什么?分别在哪个层次上面实现的?
将网络互相连接起来要使用一些中间设备(或中间系统),ISO的术语称之为中继(relay)系统。根据中继系统所在的层次,可以有以下五种中继系统:
1) 物理层(即常说的第一层、层L1)中继系统,即转发器(repeater)。 2) 数据链路层(即第二层,层L2),即网桥或桥接器(bridge)。 3) 网络层(第三层,层L3)中继系统,即路由器(router)。 4) 网桥和路由器的混合物桥路器(brouter)兼有网桥和路由器的功能。 5) 在网络层以上的中继系统,即网关(gateway).
当中继系统是转发器时,一般不称之为网络互联,因为这仅仅是把一个网络扩大了,而这仍然是一个网络。高层网关由于比较复杂,目前使用得较少。因此一般讨论网络互连时都是指用交换机和路由器进行互联的网络。本文主要阐述交换机和路由器及其区别。 2. 第二层交换机和路由器的区别:
传统交换机从网桥发展而来,属于OSI第二层即数据链路层设备。它根据MAC地址寻址,通过站表选择路由,站表的建立和维护由交换机自动进行。路由器属于OSI第三层即网络层设备,它根据IP地址进行寻址,通过路由表路由协议产生。因特网的路由选择协议:内部网关协议IGP和外部网关协议EGP 3. 第三层交换机和路由器的区别:
在第三层交换技术出现之前,几乎没有必要将路由功能器件和路由器区别开来,他们完全是相同的:提供路由功能正在路由器的工作,然而,现在第三层交换机完全能够执行传统路由器的大多数功能。
综上所述,交换机一般用于LAN-WAN的连接,交换机归于网桥,是数据链路层的设备,有些交换机也可实现第三层的交换。路由器用于WAN-WAN之间的连接,可以解决异性网络之间转发分组,作用于网络层。他们只是从一条线路上接受输入分组,然后向另一条线路转发。这两条线路可能分属于不同的网络,并采用不同协议。相比较而言,路由器的功能较交换机要强大,但速度相对也慢,价格昂贵,第三层交换机既有交换机线速转发报文能力,又有路由器良好的控制功能,因此得以广播应用。
3 高质量编程C/C++
一、请填写BOOL , float, 指针变量与―零值‖比较的 if 语句。(10 分) 请写出 BOOL flag 与―零值‖比较的 if 语句。(3 分) 标准答案: if ( flag )
if ( !flag )
如下写法均属不良风格,不得分。 if (flag == TRUE) if (flag == 1 ) if (flag == FALSE) if (flag == 0)
请写出 float x 与―零值‖比较的 if 语句。(4 分) 标准答案示例:
const float EPSINON = 0.000001; if ((x >= – EPSINON) && (x <= EPSINON) 不可将浮点变量用―==‖或―!=‖与数字 比较,应该设法转化成―>=‖或―<=‖此 类形式。
如下是错误的写法,不得分。 if (x == 0.0) if (x != 0.0)
请写出 char *p 与―零值‖比较的 if 语句。(3 分) 标准答案: if (p == NULL) if (p != NULL)
如下写法均属不良风格,不得分。 if (p == 0) if (p != 0) if (p) if (!)
二、以下为Windows NT 下的32 位C++程序,请计算sizeof 的值(10 分) void Func ( char str[100]) { 请计算
sizeof( str ) = 4 (2 分) }
char str[] = ―Hello‖ ; char *p = str ; int n = 10; 请计算
sizeof (str ) = 6 (2 分) sizeof ( p ) = 4 (2 分) sizeof ( n ) = 4 (2 分) void *p = malloc( 100 ); 请计算
sizeof ( p ) = 4 (2 分) 三、简答题(25 分)
1、头文件中的 ifndef/define/endif 干什么用?(5 分) 答:防止该头文件被重复引用。
2、#include 和 #include ―filename.h‖ 有什么区别?(5 分) 答:对于#include ,编译器从标准库路径开始搜索 filename.h 对于#include ―filename.h‖ ,编译器从用户的工作路径开始搜索 filename.h 3、const 有什么用途?(请至少说明两种)(5 分)答:(1)可以定义 const 常量,(2)const 可以修饰函数的参数、返回值,甚至函数的定义体。被const 修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
4、在C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern ―C‖? (5 分) 答:C++语言支持函数重载,C 语言不支持函数重载。函数被C++编译后在库中的名字 与C 语言的不同。假设某个函数的原型为: void foo(int x, int y);该函数被C 编译器编译后在库中的名字为_foo , 而C++编译器则会产生像_foo_int_int 之类的名字。C++提供了C 连接交换指定符号extern―C‖来解决名字匹配问题。 5、请简述以下两个for 循环的优缺点(5 分) for (i=0; iif (condition) DoSomething(); elseDoOtherthing(); }
if (condition) {
for (i=0; i{for (i=0; i优点:程序简洁缺点:多执行了N-1 次逻辑判断,并且打断了循环―流水线‖作业,使得编译器不能对循环进行优化处理,降低了效率。 优点:循环的效率高 缺点:程序不简洁
四、有关内存的思考题(每小题5 分,共20 分) void GetMemory(char *p) {
p = (char *)malloc(100); }
void Test(void) {
char *str = NULL; GetMemory(str); strcpy(str, ―hello world‖); printf(str); }
请问运行Test 函数会有什么样的结果? 答:程序崩溃。
因为GetMemory 并不能传递动态内存, Test 函数中的 str 一直都是 NULL。 strcpy(str, ―hello world‖);将使程序崩 溃。
char *GetMemory(void) {
char p[] = ―hello world‖; return p; }
void Test(void) {
char *str = NULL; str = GetMemory(); printf(str);
}
请问运行Test 函数会有什么样的结果? 答:可能是乱码。
因为GetMemory 返回的是指向―栈内存‖ 的指针,该指针的地址不是 NULL,但其原 现的内容已经被清除,新内容不可知。 void GetMemory2(char **p, int num) {
*p = (char *)malloc(num); }
void Test(void) {
char *str = NULL; GetMemory(&str, 100); strcpy(str, ―hello‖); printf(str); }
请问运行Test 函数会有什么样的结果? 答:(1)能够输出hello;(2)内存泄漏 void Test(void) {
char *str = (char *) malloc(100); strcpy(str, ―hello‖); free(str); if(str != NULL) {
strcpy(str, ―world‖); printf(str); } }
请问运行Test 函数会有什么样的结果? 答:篡改动态内存区的内容,后果难以预 料,非常危险。
因为free(str);之后,str 成为野指针, if(str != NULL)语句不起作用。 五、编写strcpy 函数(10 分) 已知strcpy 函数的原型是
char *strcpy(char *strDest, const char *strSrc); 其中strDest 是目的字符串,strSrc 是源字符串。 (1)不调用C++/C 的字符串库函数,请编写函数 strcpy char *strcpy(char *strDest, const char *strSrc); {
assert((strDest!=NULL) && (strSrc !=NULL)); // 2分 char *address = strDest; // 2分
while( (*strDest++ = * strSrc++) != ‗\\0‘ ) // 2分 NULL ;
return address ; // 2分 }
(2)strcpy 能把strSrc 的内容复制到strDest,为什么还要char * 类型的返回值? 答:为了实现链式表达式。 // 2 分
例如 int length = strlen( strcpy( strDest, ―hello world‖) ); 六、编写类String 的构造函数、析构函数和赋值函数(25 分) 已知类String 的原型为: class String { public:
String(const char *str = NULL); // 普通构造函数 String(const String &other); // 拷贝构造函数 ~ String(void); // 析构函数
String & operate =(const String &other); // 赋值函数 private:
char *m_data; // 用于保存字符串 };
请编写String 的上述4 个函数。 标准答案: // String 的析构函数 String::~String(void) // 3 分 {
delete [] m_data;
// 由于m_data 是内部数据类型,也可以写成 delete m_data; }
// String 的普通构造函数
String::String(const char *str) // 6 分 {
if(str==NULL) {
m_data = new char[1]; // 若能加 NULL 判断则更好 *m_data = ‗\\0‘; } else {
int length = strlen(str);
m_data = new char[length+1]; // 若能加 NULL 判断则更好 strcpy(m_data, str); } }
// 拷贝构造函数
String::String(const String &other) // 3 分 {
int length = strlen(other.m_data);
m_data = new char[length+1]; // 若能加 NULL 判断则更好 strcpy(m_data, other.m_data); }
// 赋值函数
String & String::operate =(const String &other) // 13 分 {
// (1) 检查自赋值 // 4 分 if(this == &other) return *this;
// (2) 释放原有的内存资源 // 3 分 delete [] m_data;
// (3)分配新的内存资源,并复制内容 // 3 分 int length = strlen(other.m_data);
m_data = new char[length+1]; // 若能加 NULL 判断则更好 strcpy(m_data, other.m_data); // (4)返回本对象的引用 // 3 分 return *this; }