(二)

1.关键字 const 和关键字 static 解析

关键字const用来定义常量,如果一个变量被const修饰,那么它的值就不能再被改变,我想一定有人有这样的疑问,C语言中不是有#define吗,干嘛还要用const呢,我想事物的存在一定有它自己的道理,所以说const的存在一定有它的合理性,与预编译指令相比,const修饰符有以下的优点:

1、预编译指令只是对值进行简单的替换,不能进行类型检查

2、可以保护被修饰的东西,防止意外修改,增强程序的健壮性

3、编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。

下面我们从几个方面来说一下const的用法:

一、修饰局部变量

const int n=5;
int const n=5;
这两种写法是一样的,都是表示变量n的值不能被改变了,需要注意的是,用const修饰变量时,一定要给变脸初始化,否则之后就不能再进行赋值了。

接下来看看const用于修饰常量静态字符串,例如:

const char* str="fdsafdsa";

如果没有const的修饰,我们可能会在后面有意无意的写str[4]=’x’这样的语句,这样会导致对只读内存区域的赋值,然后程序会立刻异常终止。有了const,这个错误就能在程序被编译的时候就立即检查出来,这就是const的好处。让逻辑错误在编译期被发现。

二、常量指针与指针常量

常量指针是指针指向的内容是常量,可以有一下两种定义方式。

const int * n;
int const * n;
需要注意的是一下两点:

1、常量指针说的是不能通过这个指针改变变量的值,但是还是可以通过其他的引用来改变变量的值的。

int a=5;
const int* n=&a;
a=6;
2、常量指针指向的值不能改变,但是这并不是意味着指针本身不能改变,常量指针可以指向其他的地址。

int a=5;
int b=6;
const int* n=&a;
n=&b;
指针常量是指指针本身是个常量,不能在指向其他的地址,写法如下:

int *const n;
需要注意的是,指针常量指向的地址不能改变,但是地址中保存的数值是可以改变的,可以通过其他指向改地址的指针来修改。

int a=5;
int *p=&a;
int* const n=&a;
*p=8;
区分常量指针和指针常量的关键就在于星号的位置,我们以星号为分界线,如果const在星号的左边,则为常量指针,如果const在星号的右边则为指针常量。如果我们将星号读作‘指针’,将const读作‘常量’的话,内容正好符合。int const * n;是常量指针,int *const n;是指针常量。

指向常量的常指针

是以上两种的结合,指针指向的位置不能改变并且也不能通过这个指针改变变量的值,但是依然可以通过其他的普通指针改变变量的值。

const int* const p;
三、修饰函数的参数

根据常量指针与指针常量,const修饰函数的参数也是分为三种情况

1、防止修改指针指向的内容

void StringCopy(char *strDestination, const char *strSource);
其中 strSource 是输入参数,strDestination 是输出参数。给 strSource 加上 const 修饰后,如果函数体内的语句试图改动 strSource 的内容,编译器将指出错误。

2、防止修改指针指向的地址

void swap ( int * const p1 , int * const p2 )
指针p1和指针p2指向的地址都不能修改。

3、以上两种的结合。

四、修饰函数的返回值

如果给以“指针传递”方式的函数返回值加 const 修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。
例如函数

const char * GetString(void);
如下语句将出现编译错误:

char *str = GetString();
正确的用法是

const char *str = GetString();
五、修饰全局变量

全局变量的作用域是整个文件,我们应该尽量避免使用全局变量,以为一旦有一个函数改变了全局变量的值,它也会影响到其他引用这个变量的函数,导致除了bug后很难发现,如果一定要用全局变量,我们应该尽量的使用const修饰符进行修饰,这样方式不必要的以为修改,使用的方法与局部变量是相同的。

static关键字:

其实static用法非常广泛,静态变量、静态成员、静态函数等,如果用的好,会使程序提升一个等级,并且可以解决很多实际问题,比如单例模式…而且静态数据成员存储在静态存储区,只存储一次,可以节省内存。所以在这里尽自己目前的理解做一个总结:

用法1:static局部变量

我的理解是有点类似全局变量的功能,但只能被赋值一次,也就是说函数第一次赋值之后,会一直保存这个值。

void f()
{
static int st=10;
st++;
cout<<st<<endl;
}
int main()
{
f();
f();
f();
}
输入结果为:11
12
13
用法2:static全局变量

静态全局变量和静态局部变量类似,只是作用域不同,都是存储在全局数据区。
关于存储区:new产生的动态数据存储在堆去;自动变量存储在栈去,会自动释放;而静态变量和全局变量存储在全局存储区,不会随函数退出而释放。
与一般全局变量的区别:

static全局变量只初始化一次
仅能在本文件中使用,其它文件可以定义相同名字的变量
用法3:static函数

只要记住一点:静态函数与普通函数的区别,只能在声明它的地方可见,不能被其它文件所使用

static void f();
int main()
{
f();
}
void f()
{
cout<<"fuck"<<endl;

}
总结1:若全局变量仅在单个文件中可用,则声明为static全局变量,若全局变量仅在单个函数内部可用,则声明为static局部变量。

OK,讲完了面向过程,该轮到面向对象了,这也是我觉得static比较精华的地方。

用法4:static数据成员

静态成员可供所有的对象访问,只分配一次内存,也只有一份拷贝,对于每个对象都是一样,它的值可以更新;
静态成员存储在全局数据区,因此不能类声明中定义,初始化规则:<数据类型><类名>::<静态数据成员名>=<值> ,在使用前必须初始化;
访问:通过对象或者类名来访问;
静态成员的访问符和普通成员一样:public、protected、private;
作用:

实现全局性,但不会与程序中其它全局名字冲突
与private搭配,可实现信息隐藏
一个计数的例子

class Example
{
public:
static int count;
Example() {
count++;
}
};
int Example::count = 0;
int main()
{
Example e1,e2;
cout<<Example::count<<endl;
}
输出:2
用法5:static成员函数

与静态成员类似,它是类内部共同享用,和普通函数不同,静态成员函数没用this指针;
访问:与静态成员类似通过类名或对象;
非静态成员函数可以任意地访问静态成员函数和静态数据成员,但反过来不行;
静态成员之间可以相互访问,包括静态成员函数访问静态数据成员和访问静态成员函数;
class Example
{
private:
int m1;
public:
static int count;
Example() {
count++;
}
static void f1();
void f2()
{
f1();//OK
}
void f3();

};
void Example::f1()
{
cout << m1 << endl;//错误调用
f3();//错误调用
cout << count << endl;//OK
}
2.先进先出、后进先出的典型数据结构分别是什么?

先进先出:队列;

后进先出:栈;

3.

http 协议状态码 200、302、404、500 分别代表什么?
200 是最常见的 http 状态码,它的含义很简单,就是成功了。一个正常网页返回的状态码就
是 200.
302 是临时性重定向到意思,就是一个网页临时被搬到了另一个网页上。

404 是错误页面返回的状态码,表示页面不存在或被删除。

500 表示服务器正在维护。

301 是 seo(网站优化)中最重要的 http 状态码,很多地方都会用到 301 重定向。这是一种
永久性的重定性。

4.

tcp 协议和 udp 协议的区别是什么?各举一个典型的应用场景。
TCP(TransmissionControlProtocol)和 UDP(UserDatagramProtocol)协议属于传输层协议。
TCP 是美国国防部设计的两种传输协议之一,另一种是 UDP。

UDP 是一种不可靠的网络服务,负载比较小,而 TCP 则是一种可靠的通信服务,负载相对而言比较大。

TCP 采用套接字(socket)或者端口(port)来建立通信。

TCP 给端口到端口通信提供了错误和流量控制机制,同时 TCP还负责建立连接、处理终止和中断的端对端通信控制。

通常情况下我们认为 TCP 相比 UDP具有更大的通信负载,UDP 不具备 TCP 的控制特性,TCP 用了大约 20 个字节来发送一个65Kbps 的数据块,这个报头占整个数据块的比重也不过 3%。总得来看,这个负载是合理的,何况还令通信具有了可靠性。

应用场景:

具体分析看: http://blog.csdn.net/u013777351/article/details/49226101

5.内存管理的页面置换算法:

5.1 Optimal算法(最优算法)

  首先介绍最优算法,它需要知道以后要被用到的页,然后将不会被用到的页换出内存;如果所有页都会被用到,就把需要使用时间离现在最长的页换出,以尽量使不好的情况晚发生。这种方法能使系统获得最佳性能,但是,它是不可能实现的......因为当前无法获知以后哪些页要被用到。不过最优算法还是能够作为其他算法优秀程度的衡量。

 

5.2 FIFO(First-In First-Out,先进先出)算法

  FIFO算法的思想很简单,就是置换出当前已经待在内存里时间最长的那个页。FIFO算法的运行速度很快,不需要考虑其他的因素,需要的开销很少。但是正是由于没有考虑页面的重要性的问题,FIFO算法很容易将重要的页换出内存。

 

5.3 Second Chance(第二次机会)算法

  为了避免FIFO算法将重要的页换出内存,Second Chance算法提供了一些改进。Second Chance算法在将页面换出内存前检查其使用位(使用位前文有介绍),如果其使用位为1,证明此页最近有被使用,猜测它还可能被使用,于是不把它置换出内存,但是把其使用位置为0,随后检查下一个页面,直到发现某页的使用位为0,将此页置换出内存。

 

5.4 Clock算法(时钟轮转法)

  为了节约Second Chance算法一个接着一个检查使用位的开销,时钟轮转法又提出了改进。时钟轮转法将所有的页组成一个圆,圆心的指针指向下一个要被置换的页面,置换前同样检查使用位,如果使用位为1,同样将其使用位置为0,随后将顺指针旋转,检查下一个页面,直到发现某页的使用位为0,将此页置换出内存。很容易理解此算法为什么叫“时钟”轮转法。

 

5.5 LRU(Least Recent Used, 最近最少使用)算法

  为获得对最优算法的模拟,提出了LRU算法。由于当前时间之后需要用到哪些页无法提前获知,于是记录当前时间之前页面的使用情况,认为之前使用过的页面以后还会被用到。在置换时,将最近使用最少的页面换出内存。此种方法的开销比较大。

 

5.6 NRU(Not Recent Used, 最近未使用)算法

  前面提到修改位和使用位,NRU算法利用这两个标志位将所有页帧分为4组:

  第0组:修改位和使用位都为0;

  第1组:修改位为0,使用位为1;

  第2组:修改位为1,使用位为0;

  第3组:修改位和使用位都为1。

  NRU算法从组数最小的一组中随机选择一个页面将其移出内存。可能有人会发现第2组这种情况根本不会出现,如果一个页帧被修改,其修改位会被置1,同时它也被使用了,其使用位也会被置1;即不会出现被修改但是没有被使用的情况。真实情况是,页帧的使用位会被定时清零,这样第3组经过一次清零就会变成第2组。这也符合“最近”未使用,即很久以前被使用的页帧被清零了,不在统计范围内,只要“最近”没有被使用,就很有可能被移出。

  NRU算法不是最好的,但是它使用起来开销很小,用较小的代价就得到了不错的效果,不失为一种不错的算法。

6.解释并比较面型对象编程和面向过程编程

1)从思维方式上来讲,是对现实问题的建模方式不同。面向过程编程把问题分解成一个个过程,一步步实现这些过程,问题就得到了解决。而面向对象编程把问题看成一系列对象之间的交互,设计出这些对象以及对象之间的交互,问题就得到了解决。

2)从程序的本质上来讲,是对算法以及数据的重视程度的不同。程序的本质是算法和数据。面向过程编程重视算法,忽视数据。面向对象编程侧重点在数据,对算法的重视程度不如面向过程编程。对数据的重视,使面向对象编程具有继承,封装,多态等特性,在各方面提高了编程的效率。

详细解析请参考: http://blog.csdn.net/u012069890/article/details/56497186 

7.互联网应用和企业级应用的区别?

企业应用系统从封闭走向开放,由局域网转到互联网,随着涉众面的极大扩展,新的企业应用要求多浏览器支持(IE,FireFox),国际化支持,全球业务的互联互通。这样就要求企业应用不能满足简单的表单、表格、树、菜单;而是要求有较好的用户体验,提倡富互联网应用。
  企业应用的内容也发生一些转变:除了企业的核心业务系统,新的企业应用也应运而生,典型的比如有:交互性门户系统(个性化门户,个人工作台等),电子商务平台,企业级2.0(博客,Wiki,RSS,微博),企业级SNS(社区平台),无线企业应用等。
  企业需求的提升:除了功能性需求,客户对于安全,性能,大容量,大并发,易维护等特性愈发关注,未来的趋势是企业应用构建在互联网而不局限于局域网,可能是在云,也可能是网格,也可能在其他的新技术上实现。
企业应用和互联网应用从根本来说是相同的,都是基于因特网、HTTP、浏览器的一种应用,但面向的涉众不一样,从而导致些许差异性,比较如下:
企业应用(表1):
  1 行业领域 区分行业,各自领域业务背景不一样,并形成了一定的门槛。
  2 业务逻辑 业务逻辑复杂,涉及大量的数据和多人协同处理。
  3 数据一致性 强调数据一致性,需要通过事务,交易中间件,数据库锁,java同步机制来保证数据的一致性。
  4 数据复杂度 数据复杂,有大量的表,表之间有复杂的牵涉关系,在某些行业维护这些表之间的关系和数据就需要一个团队。
  5 并发量 不是特别大,比如通用应用为100~200并发,重度并发500的系统就能满足国内大部分的系统要求。
  6 系统集成 关键系统需要和很多外部系统集成,集成的方式可能采取esb,jms,web service,socket。
  7 用户交互 强调界面交互和数据表达,需要支持多种数据展现方式,需要众多数据在页面上的展现,传输
  8 开发过程 强调软件过程,讲究行业经验,需要撰写大量的文档和多人的协同,需要版本控制和问题跟踪回溯。
互联网应用(表2):
  1 行业领域 跨行业,按应用类型区分,比如blog,wiki,个人门店等。
  2 业务逻辑 业务逻辑简单,大部分是通过页面进行数据的增删改查。
  3 数据一致性 要求有事务,但和高并发博弈中,让位给高并发。
  4 数据复杂度 数据不复杂,表之间的关联不多
  5 并发量 强调高并发,支持用户数量多,并采取企业开发中极少采用的技术,比如web反向代理,memcache(分布式缓存),表的垂直分隔、水平分隔,强调高速读低速写。支持百万用户。
  6 系统集成 弱。极少需要和其他系统集成
  7 用户交互 弱。交互不多,表现方式简单,更多的是数据的增删改查。
  8 开发过程 强调敏捷,快速开发,基本不需要版本控制。
通过简单的比对,由此可见,互联网开发强调的是快速,敏捷,涉众面广的一类系统。

posted @ 2018-03-09 20:57  陈焕彪  阅读(352)  评论(0编辑  收藏  举报