C++ 面向对象(二)—— 操作符重载

C++ 实现了在类(class)之间使用语言标准操作符,而不只是在基本数据类型之间使用。例如:

int a, b, c;
a = b + c;

是有效操作,因为加号两边的变量都是基本数据类型。然而,我们是否可以进行下面的操作就不是那么显而易见了(它实际上是正确的):

struct { char product [50]; float price; } a, b, c;
a = b + c;

将一个类class (或结构struct)的对象赋给另一个同种类型的对象是允许的(通过使用默认的复制构造函数 copy constructor)。但相加操作就有可能产生错误,理论上讲它在非基本数据类型之间是无效的。

但归功于C++ 的操作符重载(overload)能力,我们可以完成这个操作。像以上例子中这样的组合类型的对象在C++中可以接受如果没有操作符重载则不能被接受的操作,我们甚至可以修改这些操作符的效果。以下是所有可以被重载的操作符的列表:

	+    -    *    /    =    <    >    +=   -=   *=   /=   <<   >><<=  >>=  ==   !=   <=   >=   ++   --   %    &    ^    !    |~    &=   ^=   |=   &&   ||   %=   []   ()   new  delete

要想重载一个操作符,我们只需要编写一个成员函数,名为operator ,后面跟我们要重载的操作符,遵循以下原型定义:

type operator sign (parameters);

这里是一个操作符 +的例子。我们要计算二维向量(bidimensional vector) a(3,1) 与b(1,2)的和。两个二维向量相加的操作很简单,就是将两个x 轴的值相加获得结果的x 轴值,将两个 y 轴值相加获得结果的 y值。在这个例子里,结果是 (3+1,1+2) = (4,3)。

    // vectors: overloading operators example#include <iostream.h>class CVector {public:int x,y;CVector () {};CVector (int,int);CVector operator + (CVector);};CVector::CVector (int a, int b) {x = a;y = b;}CVector CVector::operator+ (CVector param) {CVector temp;temp.x = x + param.x;temp.y = y + param.y;return (temp);}int main () {CVector a (3,1);CVector b (1,2);CVector c;c = a + b;cout << c.x << "," << c.y;return 0;}    
4,3

如果你迷惑为什么看到这么多遍的 CVector,那是因为其中有些是指class名称CVector ,而另一些是以它命名的函数名称,不要把它们搞混了:

   CVector (int, int);            // 函数名称 CVector (constructor)CVector operator+ (CVector);   // 函数 operator+ 返回CVector 类型的值

Class CVector的函数 operator+ 是对数学操作符+进行重载的函数。这个函数可以用以下两种方法进行调用:

c = a + b;
c = a.operator+ (b);

注意:我们在这个例子中包括了一个空构造函数 (无参数),而且我们将它定义为无任何操作:

CVector ( ) { };

这是很必要的,因为例子中已经有另一个构造函数,

CVector (int, int);

因此,如果我们不像上面这样明确定义一个的话,CVector的两个默认构造函数都不存在。

这样的话,main( )中包含的语句

CVector c;

将为不合法的。

尽管如此,我已经警告过一个空语句块 (no-op block)并不是一种值得推荐的构造函数的实现方式,因为它不能实现一个构造函数至少应该完成的基本功能,也就是初始化class中的所有变量。在我们的例子中,这个构造函数没有完成对变量x 和 y 的定义。因此一个更值得推荐的构造函数定义应该像下面这样:

CVector ( ) { x=0; y=0; };

就像一个class默认包含一个空构造函数和一个复制构造函数一样,它同时包含一个对赋值操作符assignation operator (=)的默认定义,该操作符用于两个同类对象之间。这个操作符将其参数对象(符号右边的对象) 的所有非静态 (non-static) 数据成员复制给其左边的对象。当然,你也可以将它重新定义为你想要的任何功能,例如,只拷贝某些特定class成员。

重载一个操作符并不要求保持其常规的数学含义,虽然这是推荐的。例如,虽然我们可以将操作符+定义为取两个对象的差值,或用==操作符将一个对象赋为0,但这样做是没有什么逻辑意义的。

虽然函数operator+ 的原型定义看起来很明显,因为它取操作符右边的对象为其左边对象的函数operator+的参数,其它的操作符就不一定这么明显了。以下列表总结了不同的操作符函数是怎样定义声明的 (用操作符替换每个@):

ExpressionOperator (@)Function memberGlobal function
@a+ - * & ! ~ ++ --A::operator@( )operator@(A)
a@++ --A::operator@(int)operator@(A, int)
a@b+ - * / % ^ & | < > == != <= >= << >> && || ,A::operator@(B)operator@(A, B)
a@b= += -= *= /= %= ^= &= |= <<= >>= [ ]A::operator@(B)-
a(b, c...)( )A::operator()(B, C...)-
a->b->A::operator->()-

* 这里a 是class A的一个对象,b 是 B 的一个对象,c 是class C 的一个对象。

从上表可以看出有两种方法重载一些class操作符:作为成员函数(member function)或作为全域函数(global function)。它们的用法没有区别,但是我要提醒你,如果不是class的成员函数,则不能访问该class的private 或 protected 成员,除非这个全域函数是该class的 friend (friend 的含义将在后面的章节解释)。


关键字 this

关键字this 通常被用在一个class内部,指正在被执行的该class的对象(object)在内存中的地址。它是一个指针,其值永远是自身object的地址。

它可以被用来检查传入一个对象的成员函数的参数是否是该对象本身。例如:

    // this#include <iostream.h>class CDummy {public:int isitme (CDummy& param);};int CDummy::isitme (CDummy& param) {if (&param == this) return 1;else return 0;}int main () {CDummy a;CDummy* b = &a;if ( b->isitme(a) )cout << "yes, &a is b";return 0;}
yes, &a is b

它还经常被用在成员函数operator= 中,用来返回对象的指针(避免使用临时对象)。以下用前面看到的向量(vector)的例子来看一下函数operator= 是怎样实现的:

   CVector& CVector::operator= (const CVector& param) {x=param.x;y=param.y;return *this;}

实际上,如果我们没有定义成员函数operator=,编译器自动为该class生成的默认代码有可能就是这个样子的。


静态成员(Static members)

一个class 可以包含静态成员(static members),可以是数据,也可以是函数。

一个class的静态数据成员也被称作类变量"class variables",因为它们的内容不依赖于某个对象,对同一个class的所有object具有相同的值。

例如,它可以被用作计算一个class声明的objects的个数,见以下代码程序:

    // static members in classes#include <iostream.h>class CDummy {public:static int n;CDummy () { n++; };~CDummy () { n--; };};int CDummy::n=0;int main () {CDummy a;CDummy b[5];CDummy * c = new CDummy;cout << a.n << endl;delete c;cout << CDummy::n << endl;return 0;} 
7
6

实际上,静态成员与全域变量(global variable)具有相同的属性,但它享有类(class)的范围。因此,根据ANSI-C++ 标准,为了避免它们被多次重复声明,在class的声明中只能够包括static member的原型(声明),而不能够包括其定义(初始化操作)。为了初始化一个静态数据成员,我们必须在class之外(在全域范围内),包括一个正式的定义,就像上面例子中做法一样。

因为它对同一个class的所有object是同一个值,所以它可以被作为该class的任何object的成员所引用,或者直接被作为class的成员引用(当然这只适用于static 成员):

cout << a.n;
cout << CDummy::n;

以上两个调用都指同一个变量:class CDummy里的static 变量 n 。

在提醒一次,它其实是一个全域变量。唯一的不同是它的名字跟在class的后面。

就像我们会在class中包含static数据一样,我们也可以使它包含static 函数。它们表示相同的含义:static函数是全域函数(global functions),但是像一个指定class的对象成员一样被调用。它们只能够引用static 数据,永远不能引用class的非静态(nonstatic)成员。它们也不能够使用关键字this,因为this实际引用了一个对象指针,但这些 static函数却不是任何object的成员,而是class的直接成员。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/401827.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Seen.js – 使用 SVG 或者 Canvas 渲染 3D 场景

Seen.js 渲染3D场景为 SVG 或者 HTML5 画布。Seen.js 包含对于 SVG 和 HTML5 Canvas 元素的图形功能的最简单的抽象。所有这个库的其它组件都是不用关心将要渲染的上下文的类型。 您可能感兴趣的相关文章你见过吗&#xff1f;9款超炫的复选框&#xff08;Checkbox&#xff09;效…

C++ 面向对象(三)—— 类之间的关系

友元函数(Friend functions) 在前面的章节中我们已经看到了对class的不同成员存在3个层次的内部保护&#xff1a;public&#xff0c; protected 和 private。在成员为 protected 和 private的情况下&#xff0c;它们不能够被从所在的class以外的部分引用。然而&#xff0c;这个…

Oracle 之 用户与权限

1.创建用户create user zhu identified by "123456";#刚创建的新用户权限为空&#xff0c;所以不能连接数据库 grant create session to zhu;#赋予create session才可以连接到数据库2.修改用户的密码SQL> conn zhu/123456; Connected. SQL> show user; USER i…

C++ 面向对象(四)—— 多态 (Polymorphism)

基类的指针(Pointers to base class) 继承的好处之一是一个指向子类(derived class)的指针与一个指向基类(base class)的指针是type-compatible的。 本节就是重点介绍如何利用C的这一重要特性。例如&#xff0c;我们将结合C的这个功能&#xff0c;重写前面小节中关于长方形rect…

五年后存储会是什么样子

原文&#xff1a;http://chucksblog.emc.com/chucks_blog/2013/06/what-storage-might-look-like-in-five-years.html注明&#xff1a;本文内容基于 VMware VSAN beta 版本撰写&#xff0c;请访问http://www.vmware.com/products/virtual-san/获得有关正式版本的更新信息。有时…

C++ 标准函数库 —— 文件的输入输出 (Input/Output with files)

C 通过以下几个类支持文件的输入输出&#xff1a; ofstream: 写操作&#xff08;输出&#xff09;的文件类 (由ostream引申而来)ifstream: 读操作&#xff08;输入&#xff09;的文件类(由istream引申而来)fstream: 可同时读写操作的文件类 (由iostream引申而来) 打开文件(Ope…

java.lang.NoClassDefFoundError: javax/transaction/Synchronization (jUnit测试报错)

测试hibernate报错原因项目缺少包在 hibernate 解压目录下找到 jta.jar 文件往项目中添加该 jar 包&#xff0c;即可解决添加方法&#xff1a;【右击项目】-->【构建路径】....来自为知笔记(Wiz)转载于:https://www.cnblogs.com/zhanyao/p/3711322.html

C++ 高级篇(五)—— 预处理指令

预处理指令是我们写在程序代码中的给预处理器(preprocessor)的 命令&#xff0c;而不是程序本身的语句。预处理器在我们编译一个C程序时由编译器自动执行&#xff0c;它负责控制对程序代码的第一次验证和消化。 所有这些指令必须写在单独的一行中&#xff0c;它们不需要加结尾的…

yarn oom问题一例

线上部分job运行失败&#xff0c;报OOM的错误:因为是maptask报错&#xff0c;怀疑是map数量过少&#xff0c;导致oom&#xff0c;因此调整参数&#xff0c;增加map数量&#xff0c;但是问题依然存在。看来和map的数量没有关系。通过jobid查找jobhistory中对应的日志信息&#x…

C++ 高级篇(三)—— 出错处理

本节介绍的出错处理是ANSI-C 标准引入的新功能。如果你使用的C 编译器不兼容这个标准&#xff0c;则你可能无法使用这些功能。 在编程过程中&#xff0c;很多时候我们是无法确定一段代码是否总是能够正常工作的&#xff0c;或者因为程序访问了并不存在的资源&#xff0c;或者由…

kickStart脚本

kickstart是什么 许多系统管理员宁愿使用自动化的安装方法来安装红帽企业 Linux.为了满足这种需要,红帽创建了kickstart安装方法.使用kickstart,系统管理员可以创建一个文件,这个文件包含了在典型的安装过程中所遇 到的问题的答案. Kickstart文件可以存放于单一的…

C++ 高级篇(二)—— 名空间 (Namespaces)

通过使用名空间(Namespaces)我们可以将一组全局范围有效的类、对象或函数组织到一个名字下面。换种说法&#xff0c;就是它将全局范围分割成许多子域范围&#xff0c;每个子域范围叫做一个名空间(namespaces). 使用名空间的格式是&#xff1a; namespace identifier{namespace-…

32位CentOS系统安装kernel-PAE支持4g以上内存

转载自http://hi.baidu.com/yinhuama/item/31e572bb0a38f2ea4ec7fd7f 32位centos系统默认安装i386的内核不支持4g的内存 用yum安装kernel-PAE yum -y install kernel-PAE>vim /boot/grub/grub.conf修改设置为默认启动&#xff0c;default1改为default0 重启服务器 reboot再执…

C++ 高级篇(一)—— 模板(Templates)

模板(Templates)是ANSI-C 标准中新引入的概念。如果你使用的 C 编译器不符合这个标准&#xff0c;则你很可能不能使用模板。 函数模板( Function templates) 模板(Templates)使得我们可以生成通用的函数&#xff0c;这些函数能够接受任意数据类型的参数&#xff0c;可返回任意类…

Java笔试之Singleton

单例模式的出现&#xff0c;其主要的目的是为了使一个类只有一个实例存在。 实现单例模式主要有两种方式&#xff08;我所知道的&#xff09; 饿汉式&#xff1a;不管有没有需要&#xff0c;都在内部先new出一个实例public class Singleton{//私有的默认构造子 private Single…

Linux--文件结构体struct file

代码:struct file {   struct file *f_next&#xff0c;**f_pprev;   struct dentry *f_dentry;   struct file_operations *f_op;   mode_t f_mode;   loff_t f_pos;   unsigned int f_count&#xff0c;f_flags;   unsigned long f_reada&#xff0c;f_…

PPT到底是天使还是魔鬼?

说老实话&#xff0c;我非常不喜欢那些PPT的培训者一味地鼓吹PPT的重要性&#xff0c;视觉化思维的重要性&#xff0c;PPT在演讲中的重要性等等&#xff0c;我很清楚他们这么说是因为他们必须这么说&#xff0c;因为如果不把PPT的重要性强调一下&#xff0c;谁来上他的课呢&…

ultraedit正则表达式

一般使用ultraedit中的Perl风格的正则表达式&#xff0c;下面是perl正则的基本语法 perl中的元字符如下&#xff1a; ^ 表示一行的开头&#xff1b; $ 表示一行的结尾&#xff1b; ( ) 表示一个匹配块的&#xff0c;可以对匹配上的块通过$1,$2...进行读取&#xff0c;…

微软云介绍

微软云介绍 http://msdn.microsoft.com/zh-cn/ff380142 什么是云开发&#xff1f;&#xff08;概述&#xff09; 云计算是指远程运行并通过 Internet 访问的计算机和应用程序。在云计算中&#xff0c;虚拟机在大型数据中心中运行&#xff0c;并取代了物理 PC 和服务器。通过将许…

IO端口和IO内存的区别及分别使用的函数接口

IO端口和IO内存的区别及分别使用的函数接口 每个外设都是通过读写其寄存器来控制的。外设寄存器也称为I/O端口&#xff0c;通常包括&#xff1a;控制寄存器、状态寄存器和数据寄存器三大类。根据访问外设寄存器的不同方式&#xff0c;可以把CPU分成两大类。一类CPU&#xff08…