话接前篇,继续基于对象编程语法的剩余部分:
6.类的const成员函数和const对象
const数据成员:跟const常量一样,只是一个在类里(而且是在构造函数里),一个在类外而已,都必须初始化。
const成员函数:即普通成员函数后再加const。它可以读取数据成员的值,但不能修改它们。若要修改时,数据成员前必须加mutable。以指定其可被任意更改。mutable是ansi c++考虑到实际编程时,可能一定要修改const对象中的某个数据成员而设的。const成员函数可以被相同参数表的非const成员函数重载。const对象:仅能调用const成员函数,但是构造函数和析构函数是唯一不是const成员函数却可以被const对象调用的成员函数。如下一个简单的例子
#include <iostream> using namespace std;class Aclass
{ public: Aclass(int a,int b); void Print() const;void Print(); //重载const函数
void Set(int a); protected: private: const int x; mutable int y; };Aclass::Aclass(int a,int b):x(a),y(b)
{ }void Aclass::Print() const
{ y = 9; cout<<"x="<< x <<" y="<< y <<endl; }
void Aclass::Print() //实现,非const对象调用该函数而不是const函数{
y = 8; cout<<"x="<< x <<" y="<< y <<endl; }void Aclass::Set(int a)
{ y = a; }int main()
{ Aclass a(1,2);a.Print(); //x=1 y=8
const Aclass b(1,2); b.Print(); //x=1 y=9return 0;
}
*7.运算符重载
运算符重载的方法是定义一个重载运算符的函数,在需要执行被重载的运算符时,系统就自动调用该函数,以实现相应的运算。也就是说,运算符重载是通过定义函数实现的。运算符重载实质上是函数的重载。重载运算符的函数一般格式如下:
函数类型 operator 运算符名称 (形参表列){对运算符的重载处理}
例如,想将“+”用于Complex(复数)的加法运算,函数的原型可以是这样的:
Complex operator + (Complex & c1,Complex &c2);
其中,operator是关键字,时候专门用于定义重载运算符的函数的,运算符名称就是C++提供给用户的预定运算符。注意:函数名是由operator和运算符组成。上面的operator+就是函数名,意思是“对运算符+重载“。只要掌握这点,这可以发现,这类函数和其他函数在形式上没有什么区别。
此外运算符的重载可以用两种方式实现,成员函数实现和友元函数实现运算符重载比较:
1.成员函数和友元函数实现运算符重载时,其函数名都必须以关键字operator开始,后面跟合适的运算符,但参数个数友元函数实现比成员函数实现时多了一个;
2.成员函数实现时,第一操作数必须是类的对象,但友元函数实现时,只要其中一个操作数是类的对象,所以通过友元函数实现时,两个操作数可以交换,满足交换率
3.有些运算符只允许成员函数实现(如赋值运算符),有些只允许友元函数实现(如提取、插入运算符)等;
4.成员函数隐含this指针,而友元函数没有this指针;
具体的实现方式不详说,因为操作符重载是基于对象编程里面少用的一个东西,不要花太多时间来研究。运算符重载与多态性是密切相关的,而这些都是面向对象的核心。这里我们说的是基于对象,是ADT。
*8.其他的
恩,其实和类有关的概念不止上面这些的,比如:
A。除了public和private之外的protect修饰符,我没有讲到,原因很简单,保护类型是用于继承关系的,我们的基于对象中的类都是无继承的;
B。友元的概念,也很复杂,如果你的基于对象风格的代码中会需要友元,那么一般只有两种情况:一是你的类太多太多了,类型杂乱,区分度不大,这时候你已经需要面向对象的编程风格了,你要考虑继承关系了,不能在单单使用扁平化类了。第二种情况就是,你的类很少却需要用友元,那么重新设计你的类型吧,因为你之前的设计一定是不合理的。
C。恩,还有复杂的运算符重载,其实运算符重载本身就有很多面向对象的思想在里面,因此基于对象的风格里面会很少使用,最多就那么几个简单的一元操作符重载而已。
【 A Better C = C with Class = C + new/delete + 参数默认值 + 函数重载 + 少量运算符重载 + 无继承class 】
这是本系列的最后一篇了,作为收尾,我引入两个描述:
描述一:C++有四个主流部分:better C,ADT,OO,和GP,以及发展中的functional, generative,meta programming等。Better C, 只增加函数重载、引用类型、缺省参数等简单特性的类C子集。ADT C++,整个程序由平面化的具体类(concrete class)对象构成,无继承,无多态。
描述二:“C++ 三人谈”中看到恶魔曾经指出C++ 的编程范式可以分为ADT+PP,GP,OO三个方向。1、ADT+PP ADT:abstract data type; 抽象数据类型 PP:procedure programme; 面向过程的编程范式 ADT+PP 就是说面向过程的编程范式+抽象数据类型,你可以理解为c++的前身:带类的C。2、(Generic Programming,泛型编程)号称编程思想的又一次革命。是一种基于参数化(parameterization)的编程技巧,目的是将有用的算法或者数据结构尽可能地一般化,并使其最优化。3、OO:面向对象的编程。
以上思想和Essentia C++书上的思想基本一致,也就说明了一个问题:C++是一种多范式的编程语言,不同的程序员用他写出不同范式的程序,没有优劣高下之别。本系列就是上述编程范式中的第一种,你可以把他叫做C with Class也可以叫做C+ADT还可以叫做基于对象,反正意思都是一个。基本的语法组成就是:面向过程语法(就是C语法)+扁平类语法(即只有抽象和封装,没有继承和多态)。
题外话:其实用单纯的C一样可以做到基于对象,而且丝毫没有什么痛感,但是如果你就是喜欢class关键字和“.”操作符带来的快感,那么本“C++ 我想这样用”系列描述的C with Class是你不错的选择。此外,有很多大牛甚至用C实现了泛型,单继承,多态。。。。但是我真心不推荐你使用,一来太另类,没有人力物力支持,二来毕竟是模拟实现的,不仅功能有限,那种蛋疼的感觉也不是一般人能习惯的。选择一门为该范式而生的语言是正统的选择,比如骨子里就是OO的Ruby,天生就是PP的C等等。
未来寄语:最近真的是发疯似的迷上了编程范式,这个系列算是对“基于对象(PP+ADT)编程范式”的致敬吧,虽然还是很想再写篇用纯C实现基于对象编程的系列,不过也可能要沉浸在ruby(或者其他更好的OO语言)带给我的“面向对象编程范式”的盛宴之中呢。。哈哈