C++教程之类的知识最后篇
作者:bea
只要按照从左到右的修饰顺序,而所有的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:: )() co
只要按照从左到右的修饰顺序,而所有的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函数。
那么友员函数的意义呢?一个操作需要同时操作两个资源中被保护了的成员,则这个操作应该被映射为友员函数。如盖章需要用到文件和章两个资源,则盖章映射成的函数应该为文件和章的友员函数。
名字空间
前面说明了静态成员变量,它的语义是专用于某个类而又独立于类的实例,它与全局变量的关键不同就是名字多了个限定符(即“::”,表示从属关系),如A::a是A的静态成员变量,则A::a这个名字就可以表现出a从属于A。因此为了表现这种从属关系,就需要将变量定义为静态成员变量。
考虑一种情况,映射采矿。但是在陆地上采矿和在海底采矿很明显地不同,那么应该怎么办?映射两个函数,名字分别为MiningOnLand和MiningOnSeabed。好,然后又需要映射在陆地勘探和在海底勘探,怎么办?映射为ProspectOnLand和ProspectOnSeabed。如果又需要映射在陆地钻井和在海底钻井,在陆地爆破和在海底爆破,怎么办?很明显,这里通过名字来表现语义已经显得牵强了,而使用静态成员函数则显得更加不合理,为此C++提供了名字空间,格式为namespace { }。其中的为定义的名字空间的名字,而就是多条声明或定义语句。如下:
namespace OnLand { void Mining(); void Prospect(); void ArtesianWell(){} }
namespace OnSeabed { void Mining(); void Prospect(); void ArtesianWell(){} }
void OnLand::Mining() { long a = 0; a++; } void OnLand::Prospect() { long a = 0; a
++; }
void OnSeabed::Mining() { long a = 0; a++; } void OnSeabed::Prospect() { long a =
0; a++; }
上面就定义了6个元素,每个的类型都为void()。注意上面OnLand::ArtesianWell和OnSeabed::ArtesianWell的定义直接写在“{}”中,将是inline函数。这样定义的六个变量它们的名字就带有限定符,能够从名字上体现从属关系,语义表现得比原来更好,OnSeabed::Prospect就表示在海底勘探。注意也可以如下:
namespace A { long b = 0; long a = 0; namespace B { long B = 0; float a = 0.0f } }
namespace C { struct ABC { long a, b, c, d; void ABCD() { a = b = c = d =
12; } } ab; }
namespace D { void ABC(); void ABC() { long a = 0; a++; } extern float bd; }
即名字空间里面可以放任何声明或定义语句,也可以用于修饰自定义结构,因此就可以C::ABC a; a.ABCD();。应注意C++还允许给名字空间别名,比如:namespace AB = C; AB::ABC a; a.ABCD();。这里就给名字空间C另起了个名字AB,就好像之前提过的typedef一样。
还应注意自定义类型的定义的效果和名字空间很像,如struct A { long a; };将生成A::a,和名字空间一样为映射元素的名字加上了限定符,但应该了解到结构A并不是名字空间,即namespace ABC = A;将失败。名字空间就好像所有成员都是静态成员的自定义结构。
为了方便名字空间的使用,C++提供了using关键字,其后面接namespace和名字空间的名字,将把相应名字空间中的所有映射元素复制一份,但是去掉了名字前的所有限定符,并且这些元素的有效区域就在using所在的位置,如:
void main() { { using namespace C; ABC a; a.ABCD(); } ABC b; b.ABCD(); }
上面的ABC b;将失败,因为using namespace C;的有效区域只在前面的“{}”内,出了就无效了,因此应该C::ABC b; b.ABCD();。有什么用?方便书写。因为每次调用OnLand::Prospect时都要写OnLand::,显得有点烦琐,如果知道在某个区域内并不会用到OnSeabed的成员,则可以using namespace OnLand;以减小代码的繁杂度。
注意C++还提供了using更好的使用方式,即只希望去掉名字空间中的某一个映射元素的限定符而不用全部去掉,比如只去掉OnLand::Prospect而其它的保持,则可以:using OnLand::Prospect; Prospect(); Mining();。这里的Mining();将失败,而Prospect();将成功,因为using OnLand::Prospect;只去掉了OnLand::Prospect的限定符。
至此基本上已经说明了C++的大部分内容,只是还剩下模板和异常没有说明(还有自定义类型的操作符重载,出于篇幅,在《C++从零开始(十七)》中说明),它们带的语义都很少,很大程度上就和switch语句一样,只是一种算法的包装而已。下篇介绍面向对象编程思想,并给出“世界”的概念以从语义出发来说明如何设计类及类的继承体系
有用 | 无用
应注意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函数。
那么友员函数的意义呢?一个操作需要同时操作两个资源中被保护了的成员,则这个操作应该被映射为友员函数。如盖章需要用到文件和章两个资源,则盖章映射成的函数应该为文件和章的友员函数。
名字空间
前面说明了静态成员变量,它的语义是专用于某个类而又独立于类的实例,它与全局变量的关键不同就是名字多了个限定符(即“::”,表示从属关系),如A::a是A的静态成员变量,则A::a这个名字就可以表现出a从属于A。因此为了表现这种从属关系,就需要将变量定义为静态成员变量。
考虑一种情况,映射采矿。但是在陆地上采矿和在海底采矿很明显地不同,那么应该怎么办?映射两个函数,名字分别为MiningOnLand和MiningOnSeabed。好,然后又需要映射在陆地勘探和在海底勘探,怎么办?映射为ProspectOnLand和ProspectOnSeabed。如果又需要映射在陆地钻井和在海底钻井,在陆地爆破和在海底爆破,怎么办?很明显,这里通过名字来表现语义已经显得牵强了,而使用静态成员函数则显得更加不合理,为此C++提供了名字空间,格式为namespace { }。其中的为定义的名字空间的名字,而就是多条声明或定义语句。如下:
namespace OnLand { void Mining(); void Prospect(); void ArtesianWell(){} }
namespace OnSeabed { void Mining(); void Prospect(); void ArtesianWell(){} }
void OnLand::Mining() { long a = 0; a++; } void OnLand::Prospect() { long a = 0; a
++; }
void OnSeabed::Mining() { long a = 0; a++; } void OnSeabed::Prospect() { long a =
0; a++; }
上面就定义了6个元素,每个的类型都为void()。注意上面OnLand::ArtesianWell和OnSeabed::ArtesianWell的定义直接写在“{}”中,将是inline函数。这样定义的六个变量它们的名字就带有限定符,能够从名字上体现从属关系,语义表现得比原来更好,OnSeabed::Prospect就表示在海底勘探。注意也可以如下:
namespace A { long b = 0; long a = 0; namespace B { long B = 0; float a = 0.0f } }
namespace C { struct ABC { long a, b, c, d; void ABCD() { a = b = c = d =
12; } } ab; }
namespace D { void ABC(); void ABC() { long a = 0; a++; } extern float bd; }
即名字空间里面可以放任何声明或定义语句,也可以用于修饰自定义结构,因此就可以C::ABC a; a.ABCD();。应注意C++还允许给名字空间别名,比如:namespace AB = C; AB::ABC a; a.ABCD();。这里就给名字空间C另起了个名字AB,就好像之前提过的typedef一样。
还应注意自定义类型的定义的效果和名字空间很像,如struct A { long a; };将生成A::a,和名字空间一样为映射元素的名字加上了限定符,但应该了解到结构A并不是名字空间,即namespace ABC = A;将失败。名字空间就好像所有成员都是静态成员的自定义结构。
为了方便名字空间的使用,C++提供了using关键字,其后面接namespace和名字空间的名字,将把相应名字空间中的所有映射元素复制一份,但是去掉了名字前的所有限定符,并且这些元素的有效区域就在using所在的位置,如:
void main() { { using namespace C; ABC a; a.ABCD(); } ABC b; b.ABCD(); }
上面的ABC b;将失败,因为using namespace C;的有效区域只在前面的“{}”内,出了就无效了,因此应该C::ABC b; b.ABCD();。有什么用?方便书写。因为每次调用OnLand::Prospect时都要写OnLand::,显得有点烦琐,如果知道在某个区域内并不会用到OnSeabed的成员,则可以using namespace OnLand;以减小代码的繁杂度。
注意C++还提供了using更好的使用方式,即只希望去掉名字空间中的某一个映射元素的限定符而不用全部去掉,比如只去掉OnLand::Prospect而其它的保持,则可以:using OnLand::Prospect; Prospect(); Mining();。这里的Mining();将失败,而Prospect();将成功,因为using OnLand::Prospect;只去掉了OnLand::Prospect的限定符。
至此基本上已经说明了C++的大部分内容,只是还剩下模板和异常没有说明(还有自定义类型的操作符重载,出于篇幅,在《C++从零开始(十七)》中说明),它们带的语义都很少,很大程度上就和switch语句一样,只是一种算法的包装而已。下篇介绍面向对象编程思想,并给出“世界”的概念以从语义出发来说明如何设计类及类的继承体系
有用 | 无用
猜你喜欢
您可能感兴趣的文章:
- asp.net怎样提高首页性能
- 怎样在C#中执行Javascript代码
- 企业市场将是微软的另一条战线
- UML正逐渐下滑的13个理由
- .NET怎样实现 MVC页面返回不同类型的内容
- C#计算文件的MD5值实例
- DateTime日期类型格式化显示
- .NET异常处理最佳实践方案
- 浅谈C++中const类型变量和函数重载
- 什么是C++中的虚拟克隆
- C++是什么《我的第一本C++书》
- C++中char*,String,int,CString间转换
- 解决C++客户端到C#服务器中文乱码
- 用C++如何实现八皇后问题
- C++如何动态创建二维数组
- 总结C++中指针的用法
- 如何用C++读取注册表信息
- 对C++初学者的一些忠告
- 说说C#和C++怎样混合编程