C++学习(33)

来源:互联网 发布:淘宝上的迪奥是真的吗 编辑:程序博客网 时间:2024/06/05 10:30

1.    局部变量作用域会覆盖同名的全局变量。(在同一个源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量不起作用。)


2.动态绑定实现了方法的定义与具体的对象无关,而对方法的调用则可以关联于具体的对象。

面向对象有三种特性:封装,继承,多态

其中多态性有两种,一种是依靠函数重载实现的静态绑定,一种是依靠虚函数实现的动态绑定。动态绑定跟对象的引用类型无关,它会根据具体的对象调用对应的方法。

 

静态绑定是在编译阶段所执行的函数就已经被确定了的,而动态绑定是在程序执行时才决定使用哪个函数。

使用virtual关键字可以使系统使用迟绑定技术。如果没有这个关键字,编译器就会直接将父类的函数进行绑定,而使用virtual关键字的函数在编译的时候就不会进行绑定,而在程序运行的时候,根据具体的调用对象来调用不同的方法,这就是c++的多态性,而在java中没有指针,就不会有这样的问题

 

3.(1)输出2个字节

#include<iostream>#include<string.h>using namespace std;int i=1;struct s1{  union{     short a,b;  };};int main() {  cout<<sizeof(s1);  return 0;}


(2)输出4个字节。

#include<iostream>#include<string.h>using namespace std;int i=1;struct s1{  union{     unsigned int c:32,d:1;  };};int main() {  cout<<sizeof(s1);  return 0;}


(3)输出4字节

#include<iostream>#include<string.h>using namespace std;int i=1;struct s1{  union{     short a,b;     unsigned int c:32,d:1;  };};int main() {  cout<<sizeof(s1);  return 0;}

综上分析:实际为只有short a,b;其大小为2个字节,占用的是short类型的大小;当引入unsigned int,此时联合体的大小为4个字节,不管定义了几个这样类型的变量,只要它占用的位数为1-32,会使得它的贡献的字节数为4个字节,即最大变量类型的大小。

 

 

为什么要字节对齐?

为了提高效率,计算机从内存中取数据时按照一个固定长度的。它每次取32个位,也就是4个字节。字节对齐的好处?--------以int型数据为例,如果它在内存中存放的位置按4字节对齐,也就是说1个int的数据全部落在计算机一次取数的区间内,那么只需要取一次就可以了。

如果不对齐,很不巧,这个int数据刚好跨越了取数的边界,这样就需要取两次才能把这个int的数据全部取到,这样效率也就降低了。

内存对齐是会浪费一些空间,但是这种空间上的浪费却可以减少取数的时间,这是典型的一种以空间换时间的做法。

 

结构体的对齐

1)、结构体内成员按自身长队对齐。自身长度,如char=1, shor=2,int =4 ,double=8。所谓自身对齐,指的是该成员的起始位置的内存地址必须是它自身长度的整数倍。如int只能以0,4,8这类的地址开始。

2)、结构体的总大小为结构体的有效对齐值的整数倍

 

结构体的有效对齐值的确定

 

1) 未明确指定,以结构体中最长的成员的长度为其有效值。

2) 当#pragma pack(n)指定时,以n和结构体中最长的成员的长度较小者为其值。

3) 当用_attribute__((_packed_))指定长度时,强制按照此值为结构体的有效对齐值

 

4. 内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用处。编译时,类似宏替换,使用函数体替换调用处的函数名。一般在代码中用inline修饰,但是能否形成内联函数,需要看编译器对该函数定义的具体处理。内联扩展是用来消除函数调用时的时间开销。它通常用于频繁执行的函数。一个小内存空间的函数非常受益。

 

内联函数只适合于只有1~5行的小函数。对一个含有许多语句的大函数,函数调用和返回的开销相对来说微不足道,所以也没有必要用内联函数实现。

在内联函数内不允许用循环语句和开关语句。否则编译将该函数视同普通函数。

 

5.(1)

#include<iostream>#include<string.h>using namespace std;char *getMem(void) {  char p[]="hello world";  p[5]=0x0;  return p;}void test(void) {  char *s=0x0;  s=getMem();  printf(s);}int main() {  test();  return 0;}

分析[Waring]:address of localvariable ‘p’returned.

p是个数组,在{}里面定义是个局部变量,说明这个函数执行完毕之后局部变量销毁p,这个数组中的值都没有了。

p虽然是个数组,但是单独用p这个变量,值是p这块数组的首地址,因为返回的是值传递,所以这个首地址被传到了下面的s中,s指向这个内存,而这个内存在getMem函数调用结束后就销毁了,里面存放的不知道是什么了。所以打印的话不一定出现什么。

 

(2)

#include<iostream>#include<string.h>using namespace std;char *getMem(void) {  char *p="hello world";  return p;}void test(void) {  char *s=0x0;  s=getMem();  printf(s);}int main() {  test();  return 0;}
分析:成功 helloworld字符串存放在常量区,这个地方存放的内容并不会在函数结束后销毁。p最后依然指向这里。

 

6.采用多路复用I/O监听3个套接字的数据时,如果套接字描述符分别是:5,17,19,则

select(int maxfd,struct fd_set* rdset,NULL,NULL)中的maxfd应取为:20、

分析:想要知道是是否可以从标准输入和一些套接字(sockfd)中读取数据,可以把文件描述符和sockfd加入readfds中。numfds的数值设成readfds中文件描述符中最大的那个加上一,也就是sockfd+1(因为标准输入的文件描述符的值为0,所以其他任何的文件描述符都会比标准输入的文件描述符大)。

 

Maxfd要监视的文件描述符的范围,一般取监视的描述符数的最大值+1。

点击打开链接

 

7.(1)vector就是动态数组.它也是在堆中分配内存,元素连续存放,有保留内存,如果减少大小后,内存也不会释放.如果新值>当前大小时才会再分配内存.

拥有一段连续的内存空间,并且起始地址不变,因此它能非常好的支持随即存取,即[]操作符,但由于它的内存空间是连续的,所以在中间进行插入和删除会造成内存块的拷贝,另外,当该数组后的内存空间不够时,需要重新申请一块足够大的内存并进行内存的拷贝。这些都大大影响了vector的效率。


(2)Set是一种关联容器,它用于存储数据,并且能从一个数据集合中取出数据。它的每个元素的值必须唯一,而且系统会根据该值来自动将数据排序。每个元素的值不能直接被改变。

【重点】内部结构采用红黑树的平衡二叉树。multiset跟set类似,唯一的区别是允许键值重复!!!

红黑树的操作时间跟二叉查找树的时间复杂度是一样的,执行查找、插入、删除等操作的时间复杂度为O(logn)。红黑树是特殊的AVL树,遵循红定理和黑定理。红定理:不能有两个相连的红节点。黑定理:根节点必须是黑节点,而且所有节点通向NULL的路径上,所经过的黑节点的个数必须相等。


(3)hashmap中查找元素,分为四步:

1).先根据key值计算出hash值以及h值(h值是java实现中处理得到的更优的index索引值);

2).查找table数组中的h位置,得到相应的键值对链表;

3).根据key值,遍历键值对链表,找到相应的键值对 ;

4).从键值对中取出value值。 只有以上四步都能在O(1)时间内完成,hashmap才能拥有O(1)的时间复杂度。


(4)deque是双端队列,从队尾或队首插入删除元素都是O(1).

原创粉丝点击