C++ 我想这样用(七)

时间:2023-01-29 23:15:15
C++ 我想这样用(七)

话接前篇,继续基于对象编程语法的剩余部分:

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=9

return 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语言)带给我的“面向对象编程范式”的盛宴之中呢。。哈哈