C++学习从零开始(一)2_C/C++语言_黑客防线网安服务器维护基地--Powered by WWW.RONGSEN.COM.CN

C++学习从零开始(一)2

作者:黑客防线网安C/C++教程基地 来源:黑客防线网安C/C++教程基地 浏览次数:0

本篇关键词:开始学习long函数
黑客防线网安网讯:   由于上面对实例依赖的消除,即成员函数去掉this参数,成员变量映射的是一确切的内为A::a,类型为long,映射的地址并没有给出,即还未定义,所以必须在全局空间中(即不在任何一个函数体...
   由于上面对实例依赖的消除即成员函数去掉this参数成员变量映射的是一确切的内为A::a,类型为long,映射的地址并没有给出,即还未定义,所以必须在全局空间中(即不在任何一个函数体内)再定义一遍,进而有long A::a;同样A::ABC的类型为void(),被去除了this参数,进而在A::ABC中的b = 10;等同于A::b = 10;,发现A::b是偏移类型,需要this参数,则等同于this->A::b = 10;结果A::ABC没有this参数,错误。而对于a = 10;,等同于A::a = 10;,而已经有这个变量,故没任何问题。
注意上面的a.a = 10;等同于a.A::a = 10;,而A::a不是偏移类型,那这里不是应该报错吗?对此C++特别允许这种类型不匹配的现象,其中的“a.”等于没有,因为这正是前面我们要表现的静态成员。即A a, b; a.a = 10; b.a = 20;执行后,a.a为20,因为不管哪个实例,对成员A::a的操作都修改的同一个地址所标识的内存。
什么意义?它们和普通的变量的区别就是名字被A::限定,进而能表现出它们的是专用于类A的。比如房子,房子的门的高度和宽度都定好了,有两个房子都是某个公司造的,它们的门的高度和宽度相同,因此门的高度和宽度就应该作为那个公司造的房子的静态成员以记录实际的高度和宽度,但它们并不需要因实例的不同而变化。
除了成员,C++还提供了静态局部变量。局部变量就是在函数体内的变量,被一对“{}”括起来,被限制了作用域的变量。对于函数,每次调用函数,由于函数体内的局部变量都是分配在栈上,按照之前说的,这些变量其实是一些相对值,则每次调用函数,可能由于栈的原因而导致实际对应的地址不同。
如下:
void ABC() { long a = 0; a++; } void BCD() { long d = 0; ABC(); }
void main() { ABC(); BCD(); }
上面main中调用ABC而产生的局部变量a所对应的地址和由于调用BCD,而在BCD中调用ABC而产生的a所对应的地址就不一样,原理在《C++从零开始(十五)》中说明。因此静态局部变量就表示那个变量的地址不管是通过什么途径调用它所在的函数,都不变化。如下:
void ABC() { static long a = 0; a++; } void BCD() { long d = 0; d++; ABC(); }
void main() { ABC(); BCD(); }
上面的变量a的地址是固定值,而不再是原来那种相对值了。这样从main中调用ABC和从BCD中调用ABC得到的变量a的地址是相同的。上面等同于下面:
long g_ABC_a = 0; void ABC() { g_ABC_a++; } void BCD() { long d = 0; d++;
ABC(); }
void main() { ABC(); BCD(); }
  因此上面ABC中的静态局部变量a的初始化实际在执行main之前就已经做了,而不是想象的在第一次调用ABC时才初始化,进而上面代码执行完后,ABC中的a的值为2,因为ABC的两次调用。
它的意义?表示这个变量只在这个函数中才被使用,而它的生命期又需要超过函数的执行期。它并不能提供什么语义(因为能提供的“在这个函数才被使用”使用局部变量就可以做到),只是当某些算法需要使用全局变量,而此时这个算法又被映射成了一个函数,则使用静态变量具有很好的命名效果--既需要全局变量的生存期又应该有局部变量的语义。
inline(嵌入)函数调用的效率较低,调用前需要将参数按照调用规则存放起来,然后传递存放参数的内存,还要记录调用时的地址以保证函数执行完后能回到调用处(关于细节在《C++从零开始(十五)》中讨论),但它能降低代码的长度,尤其是函数体比较大而代码中调用它的地方又比较多,可以大幅度减小代码的长度(就好像循环10次,如果不写循环语句,则需要将循环体内的代码复制10遍)。但也可能倒过来,调用次数少而函数体较小,这时之所以还映射成函数是为了语义更明确。此时可能更注重的是执行效率而不是代码长度,为此C++提供了inline关键字。
  在函数定义时,在定义语句的前面书写inline即可,表示当调用这个函数时,在调用处不像原来那样书写存放、传递参数的代码,而将此函数的函数体在调用处展开,就好像前面说的将循环体里的代码复制10遍一样。这样将不用做传递参数等工作,代码的执行效率将提高,但最终生成的代码的长度可能由于过多的展开而变长。如下:
void ABCD(); void main() { ABCD(); } inline void ABCD() { long a = 0; a++; }
上面的ABCD就是inline函数。注意ABCD的声明并没有书写inline,因为inline并不是类型修饰符,它只是告诉编译器在生成这个函数时,要多记录一些信息,然后由连接器根据这些信息在连接前视情况展开它。注意是“视情况”,即编译器可能足够智能以至于在连接时发现对相应函数的调用太多而不适合展开进而不展开。对此,不同的编译器给出了不同的处理方式,对于VC,其就提供了一个关键字__forceinline以表示相应函数必须展开,不用去管它被调用的情况。
前面说过,对于在类型定义符中书写的函数定义,编译器将把它们看成inline函数。变成了inline函数后,就不用再由于多个中间文件都给出了函数的定义而不知应该选用哪个定义所产生的地址,因为所有调用这些函数的地方都不再需要函数的地址,函数将直接在那里展开。
  const(常量)前面提到某公司造的房子的门的高度和宽度应该为静态成员变量,但很明显,在房子的实例存在的整个期间,门的高度和宽度都不会变化。C++对此专门提出了一种类型修饰符--const。它所修饰的类型表示那个类型所修饰的地址类型的数字不能被用于写操作,
即地址类型的数字如果是const类型将只能被读,不能被修改。如:const long a = 10, b = 20; a++; a = 4;(注意不能cosnt long a;,因为后续代码都不能修改a,而a的值又不能被改变,则a就没有意义了)。这里a++;和a = 4;都将报错,因为a的类型为cosnt long,表示a的地址所对应的内存的值不能被改变,而a++;和a = 4;都欲改变这个值。
  由于const long是一个类型,因此也就很正常地有const long*,表示类型为const long的指针,因此按照类型匹配,有:const long *p = &b; p = &a; *p = 10;。这里p = &a;按照类型匹配很正常,而p是常量的long类型的指针,没有任何问题。但是*p = 10;将报错,因为*p将p的数字直接转换成地址类型,也就成了常量的long类型的地址类型,因此对它进行写入操作错误。
注意:const long* const p = &a; p = &a; *p = 10;,按照从左到右修饰的顺序,上面的p的类型为const long* const,是常量的long类型的指针的常量,表示p的地址所对应的内存的值不能被修改,因此后边的p = &a;将错误,违反const的意义。同样*p = 10;也错误。不过可以:
long a = 3, *const p = &a; p = &a; *p = 10;
上面的p的类型为long* const,为long类型的常量,因此其必须被初始化。后续的p = &a;将报错,因为p是long* const,但*p = 10;却没有任何问题,因为将long*转成long后没有任何问题。所以也有:
const long a = 0; const long* const p = &a; const long* const *pp = &p;
只要按照从左到右的修饰顺序,而所有的const修饰均由于取内容操作符“*”的转换而变成相应类型中指针类型修饰符“*”左边的类型,因此*pp的类型是const long* const,*p的类型是const long。
应注意C++还允许如下使用:
struct A { long a, b; void ABC() const; };
void A::ABC() const { a = 10; b = 10; }
  上面的A::ABC的类型为void( A:: )() const,其等同于:
void A_ABC( const A *this ) { this->a = 10; this->b = 10; }
因此上面的a = 10;和b = 10;将报错,因为this的类型是const A*。上面的意思就是函数A::ABC中不能修改成员变量的值,因为各this的参数变成了const A*,但可以修改类的静态成员变量的值,如:
struct A { static long c; long a, b; void ABC() const; } long A::c;
void A::ABC() const { a = b = 10; c = 20; }
等同于:void A_ABC( const A *this ) { this->a = this->b = 10; A::c = 20; }。故依旧可以修改A::c的值。
有什么意义?出于篇幅,有关const的语义还请参考我写的另一篇文章《语义的需要》。
friend(友员)发信机具有发送电波的功能,收信机具有接收电波的功能,而发信机、收信机和电波这三个类,首先发信机由于将信息传递给电波而必定可以修改电波的一些成员变量,但电波的这些成员应该是protected,否则随便一个石头都能接收或修改电波所携带的信息。同样,收信机要接收电波就需要能访问电波的一些用protected修饰的成员,这样就麻烦了。如果在电波中定义两个公共成员函数,让发信机和收信机可以通过它们来访问被protected的成员,不就行了?这也正是许多人犯的毛病,既然发信机可以通过那个公共成员数修改电波的成员,那石头就不能用那个成员函数修改电波吗?这等于是原来没有门,后来有个门却不上锁。为了消除这个问题,C++提出了友员的概念。
在定义某个自定义类型时,在类型定义符“{}”中声明一个自定义类型或一个函数,在声明或定义语句的前面加上关键字friend即可。
如:
class Receiver; class Sender;
class Wave { private: long b, c; friend class Receiver; friend class Sender; };
上面就声明了Wave的两个友员类,以表示Receiver和Sender具备了Wave的资格,即如下:
class A { private: long a; }; class Wave : public A { … };
void Receiver::ABC() { Wave wav; wav.a = 10; wav.b = 10; wav.A::a = 10; }
上面由于Receiver是Wave的友员类,所以在Receiver::ABC中可以直接访问Wave::a、Wave::b,但wav.A::a = 10;就将报错,因为A::a是A的私有成员,Wave不具备反问它的权限,而Receiver的权限等同于Wave,故权限不够。
同样,也可有友员函数,即给出函数的声明或定义,在语句前加上friend,如下:
class Receiver { public: void ABC(); };
class A { private: long a; friend void Receiver::ABC(); };
这样,就将Receiver::ABC作为了A的友员函数,则在Receiver::ABC中,具有类A具有的所有权限。
应注意按照给出信息的思想,上面还可以如下:
class A { private: long a; friend void Receiver::ABC() { long a = 0; } };
这里就定义了函数Receiver::ABC,由于是在类型定义符中定义的,前面已经说过,Receiver::ABC将被修饰为inline函数。那么友员函数的意义呢?一个操作需要同时操作两个资源中被保护了的成员,则这个操作应该被映射为友员函数。如盖章需要用到文件和章两个资源,则盖章映射成的函数应该为文件和章的友员函数。
    黑客防线网安服务器维护方案本篇连接:http://www.rongsen.com.cn/show-15048-1.html
网站维护教程更新时间:2012-04-04 22:49:05  【打印此页】  【关闭
我要申请本站N点 | 黑客防线官网 |  
专业服务器维护及网站维护手工安全搭建环境,网站安全加固服务。黑客防线网安服务器维护基地招商进行中!QQ:29769479

footer  footer  footer  footer