Class(二) Overloaded Operators User-Defined Conversions 前言 超載運算子

Slides:



Advertisements
Similar presentations
第一單元 建立java 程式.
Advertisements

C++程序设计 王希 图书馆三楼办公室.
Class(類別) 前言 Class 的定義 Class 物件 this pointer Constructors Destructor
走向C++之路 WindyWinter WindyWinter感谢诸位前来捧场。
Visual C++ introduction
簡易C++除錯技巧 長庚大學機械系
Chap 18 類別與物件 夫有土者,有大物也。有大物者,不可以物。 物而不物,故能物物。 明乎物物者之非物也,豈獨治天下百姓而已哉!
4.1 概述 4.2 类与对象的实现 4.3 对象的初始化和析构 4.4 类的包含 4.5 类模板
Derived Class 前言 衍生類別的定義 單一繼承 public, protected, 和 privated 基底類別
列舉(enum).
刘胥影 东南大学计算机学院 面向对象程序设计1 2011~2012第3学期 刘胥影 东南大学计算机学院.
第11章 运算符重载 什么是运算符重载 运算符重载的方法 几个特殊的运算符的重载 自定义类型转换运算符 运算符重载实例.
刘胥影 东南大学计算机学院 面向对象程序设计1 2010~2011第3学期 刘胥影 东南大学计算机学院.
Operator Overloading; String and Array Objects
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
C語言簡介 日期 : 2018/12/2.
Object-Oriented Programming in C++ 第一章 C++的初步知识
程序设计期末复习 黎金宁
第三章 C++中的C 面向对象程序设计(C++).
第12章 從C到C++語言 12-1 C++語言的基礎 12-2 C++語言的輸出與輸入 12-3 C++語言的動態記憶體配置
2 C++ 的基本語法和使用環境 親自撰寫和執行程式是學好程式語言的不二法門。本章藉由兩個簡單的程式,介紹C++ 程式的基本結構和開發環境,讓初學者能逐漸建立使用C++ 的信心。
類別(class) 類別class與物件object.
Methods 靜宜大學資工系 蔡奇偉副教授 ©2011.
第二章 C++对C 在非面向对象方面的改进 更简洁,更安全.
C++程序设计 string(字符串类) vector(容器类).
Java 程式設計 講師:FrankLin.
JAVA 程式設計與資料結構 第四章 陣列、字串與數學物件.
Instructor Textbook Requirements TAs C++程式設計風格與藝術 (O’Reilly).
第一單元 建立java 程式.
10 多載函數 10.1 多載概論 多載一般函數 多載成員函數 10-3
第三章 C# 基础知识.
Classes (2) Lecture 7.
第 19 章 XML記憶體執行模式.
Java變數 2014/6/24.
第六章 簡介運算子超載 (Operator Overloading)
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
CH05. 選擇敘述.
挑戰C++程式語言 ──第8章 進一步談字元與字串
保留字與識別字.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
第11章 從C到C++語言 11-1 C++語言的基礎 11-2 C++語言的資料型態與運算子 11-3 C++語言的輸出與輸入
Class & Object 靜宜大學資工系 蔡奇偉副教授 ©2011.
樣版.
第三章 数据抽象.
字符串 (String) 字符串是 n (  0 ) 个字符的有限序列, 记作 S = “c1c2c3…cn” 其中,S 是串名字
C++语言程序设计教程 第2章 数据类型与表达式 第2章 数据类型与表达式 制作人:杨进才 沈显君.
C qsort.
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
OOP9 類別Class.
MiRanda Java Interface v1.0的使用方法
Review 1~3.
第九章 物件導向-進階.
C/C++基礎程式設計班 C++: 物件的使用、參考、重載函式 講師:林業峻 CSIE, NTU 3/28, 2015.
第 9 章 建構函式與解構函式.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第 3 章 类的基础部分 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
#include <iostream.h>
第二章 Java基本语法 讲师:复凡.
C++语言程序设计 C++语言程序设计 第二章 基本数据类型与表达式 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
本章主題 C++的程式結構 資料型態與宣告 算術運算 簡易的輸入輸出指令 程式編譯(Compile)的過程與原理.
Class 2005/05/25.
第四章 陣列、指標與參考 4-1 物件陣列 4-2 使用物件指標 4-3 this指標 4-4 new 與 delete
What is “this”? 在物件導向程式設計中,類別的定義就是在說明如果創建了“這個物件”的話,它會具有那些屬性與功能,以及這些功能是如何實現的。 而所謂的“這個物件”就以 this 來表示。 當我們在JavaScript與jQuery中寫 script 程式(函式)時,“誰”呼叫這個函式,這個“誰”就是該函式中所謂的.
C++语言程序设计 C++语言程序设计 第十一章 异常处理 C++语言程序设计.
String類別 在C語言中提供兩種支援字串的方式 可以使用傳統以null結尾的字元陣列 使用string類別
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
方法(Method) 函數.
InputStreamReader Console Scanner
C++语言程序设计 C++语言程序设计 第二章 基本数据类型与表达式 第十一组 C++语言程序设计.
Presentation transcript:

Class(二) Overloaded Operators User-Defined Conversions 前言 超載運算子 物件導向程式語言講義 2018/12/2 Class(二) Overloaded Operators 前言 超載運算子 超載運算子的定義方式 超載運算子的範例 User-Defined Conversions 靜宜大學資訊管理學系 蔡奇偉 副教授

前言 C++ 允許你為自定的資料型態(enum 或 class)定義運算子,使其能夠像基本資料型態一般地以算式的形式來處理物件。你只能 overload C++ 現有的運算子,而不能自創運算子。比方說,你不能定義 ** 運算子,因為它並不是 C++ 的運算子。我們在下一頁中,列出 C++ 所允許和不允許 overloaded 的運算子。 此外, overloaded 運算子的運算優先順序完全遵照 C++ 既有的規定;你無法做任何的變更。同樣地,你也不能把原先是單元運算子(如 ~)定義成二元運算子,反之亦然。

超載運算子 C++ 允許超載運算子如下: + - * / % ^ & | ~ + - * / % ^ & | ~ ! , = == != < > <= >= ++ -- << >> && || += -= *= /= %= ^= &= |= <<= >>= [] () -> ->* new new[] delete delete[] C++ 不允許超載的運算子有四: :: .* . ?:

超載運算子的定義方式 二元運算子 假定 @ 代表一個二元運算子、X 和 Y 是兩個類別。你可以用以下兩種方式來宣告 X 和 Y 之間的運算 @: 成員運算子: class X { public: return_type operator@ (Y); }; 非成員運算子: return_type operator@ (X, Y);

如果兩種運算子的宣告你都提供,則對算式 aa @ bb 而言,C++ 編譯器會根據 aa 和 bb 的類型,解讀成下面兩種函式呼叫中的一種: aa.operator@(bb) // 呼叫成員運算子 operator@(aa, bb) // 呼叫非成員運算子 譬如: class X { public: X(int); void operator+ (int); }; void operator+ (X, X); void operator+ (X, double); void f (X a) { a + 1; // a.operator+(1) 1 + a; // ::operator+(X(1), a) a + 1.0; // ::operator+(a, 1.0) }

prefix 單元運算子 假定 @ 代表一個 prefix 單元運算子(如 ~ 和 ++k 的 ++)、X 是一個類別。你可以用以下兩種方式來宣告 X 的運算 @: 成員運算子: class X { public: X operator@ (); // no argument }; 非成員運算子: X operator@ (X); // one argument C++ 編譯器會根據 aa 的類型把 @aa 解讀成 aa.operator@() 或 operator@(aa) 。

postfix 單元運算子 假定 @ 代表一個 postfix 單元運算子(如 k++ 的 ++)、X 是一個類別。你可以用以下兩種方式來宣告 X 的運算 @: 成員運算子: class X { public: X operator@ (int); // one argument }; 非成員運算子: X operator@ (X, int); // two argument C++ 編譯器會根據 aa 的類型把 aa@ 解讀成 aa.operator@(int) 或 operator@(aa, int) 。

一般而言,你應該把運算值為 lvalue 的運算子定義成類別的成員。這些運算子包括: = [] () -> += -= *= /= %= ^= &= |= <<= >>= 等等。其中前四者一定要定義成類別的成員。 其他運算值為 rvalue 的運算子則最好不要定義成類別的成員,如:+, -, *, / 等。

範例 class X { X* operator& (); // prefix unary & (address of) X operator& (X); // binary & (and) X operator++ (int); // postfix increment X operator& (X, X); // error: ternary X operator/ (); // error: unary }; X operator- (X); // prefix unary minus X operator- (X, X); // binary minus X operator-- (X&, int); // postfix decrement X operator- (); // error: no operand X operator- (X, X, X); // error: ternary X operator% (X); // error: unary %

超載運算子的範例 接下來我們用 CPoint2D 和 CString 兩個類別來示範如何設計超載運算子。前者我們已在上一章定義過它的結構。後者是模擬 C++ 標準類別庫中的 string 類別。它的基本結構如下: class CString { public: CString (const char * = 0); // default constructor CString (const CString &); // copy constructor ~CString (); // destructor char *c_str() { return (_size)? _string : “”; } private: int _size; char *_string };

// File: CString.cpp #include <string.h> CString::CString (const char *s) { if (s) { _size = strlen(s); _string = new char[_size+1]; strcpy(s, _string); } else { _size = 0; _string = 0; } CString::CString (const CString &s) delete [] _string; _size = s._size; strcpy(s._string, _string); CString::~CString () { delete [] _string; _size = 0; _string = 0; }

Assignment Operator = 指定運算子(=)是用來把等號右邊的物件值複製給等號左邊的物件。若沒有定義此運算子時,C++ 會自動提供一個預設的指定運算子,把等號右邊物件的資料成員一一地複製給等號左邊物件中對應的資料成員。比方說,CPoint2D 類別若沒有定義指定運算子,則 CPoint2D a(3, 4), b(4, 5); b = a; // b 變成 (3, 4),因為 b._x = a._x; b._y = a._y; 這樣的指定方式正好適用,因此我們就不需要再替CPoint2D 類別定義指定運算子。

但是對 CString 這種擁有動態配置記憶體的類別,預設的指定運算子顯然就不適用了(理由請參閱上一章有關 copy constructor 的討論)。因此我們必須為 CString 類別定義適用的指定運算子。 X 類別的指定運算子宣告方式如下: class X { public: X& operator= (const X&); // … } 註:如前所述,operator= 必須定義成類別的成員函式。

CString& CString::operator= (const CString &s) { if (this != &s) { // not assign to self delete [] _string; _size = s._size; if (_size) { _string = new char[_size+1]; strcpy(s._string, _string); } else _string = 0; return *this;

我們來探討以下的指定運算: CString s1(“Hello”); // 呼叫 CString(“Hello”) 來設定 s1 的初值 CString s2, s3; // 呼叫 CString(0) 來設定兩者的初值 s2 = s1; // 呼叫 s2.operator=(s1),故 s2 = “Hello” s3 = “World”; // s3.operator=(CString(“World”)) 最後一行的指定運算先呼叫 CString(“World”) 把字串 “World” 轉換成 CString 物件,然後再呼叫 s3.operator= 把此物件指定給 s3。顯然這個指定過程的效率並不好。由於指定字串給CString 物件是一項使用頻繁的算式,似乎值得我們專門為這種形式的指定運算子寫一個函式。

有了以上的 operator= 定義之後,前一頁的 s3 指定就變成: CString& CString::operator= (const char *s) { delete [] _string; _size = strlen(s); if (_size) { _string = new char[_size+1]; strcpy(s, _string); } else _string = 0; return *this 有了以上的 operator= 定義之後,前一頁的 s3 指定就變成: s3 = “World”; // s3.operator=( “World”) 而省掉呼叫 CString(“World”) 的過程。

Operator += CPoint2D 的 += 運算子 class CPoint2D { public: CPoint2D& CPoint2D::operator+= (const CPoint2D &); … }; CPoint2D& CPoint2D::operator+= (const CPoint2D &p) { _x += p._x; _y += p._y; return *this; }

測試程式 #include <iostream> #include “CPoint2D.h” int main () { CPoint2D a(3,4), b(5,6); a += b; cout << a; } 輸出結果: (8, 10)

CString 的 += 運算子 class CString { public: CString& CString::operator+= (const CString &s); { return concat(s._string, s._size); } CString& CString::operator+= (const char *s) { return concat(s, strlen(s)); } … private: CString& concat (const char *, int); };

CString& CString::concat (const char *s, int len) { if (len > 0) { char *tmp = _string; // backup the old string _size += len; _string = new char[_size+1]; strcpy(tmp, _string); strcat(_string, s); delete [] tmp; // delete the old string } return *this;

Operator + CPoint2D 的 + 運算子 class CPoint2D { public: friend CPoint2D operator+ (const CPoint2D &, const CPoint2D &); … }; CPoint2D operator+ (const CPoint2D &p1, const CPoint2D &p2) { CPoint2D p(p1); p._x += p2._x; p._y += p2._y; return p; }

CString 的 + 運算子 class CString { public: friend CString operator+ (const CString &cs1, const CString &cs2); friend CString operator+ (const CString &cs, const char *s); friend CString operator+ (const char *s, const CString &cs); friend CString cs_add (const char *, int, const char *, int); … }; 這些 + 運算子允許以下的算式: CString s1, s2; s1 + s2 s1 + “July” “July” + s1

// File: CString.cpp CString cs_add (const char *s1, int len1, const char *s2, int len2) { CString sobj; sobj._size = len1 + len2; if (sobj._ size) { sobj._string = new char[sobj._ size+1]; strcpy(“”, sobj._string); if (s1) strcat(sobj._string, s1); if (s2) strcat(sobj._string, s2); } return sobj;

// File: CString.h inline CString operator+ (const CString &cs1, const CString &cs2) { return cs_add(cs1._string, cs1._size, cs2._string, cs2._size) } inline CString operator+ (const CString &cs, const char *s) return cs_add(cs._string, cs._size, s, strlen(s)) inline CString operator+ (const char *s, const CString &cs) return cs_add (s, strlen(s) , cs._string, cs._size)

Operator ==, != CPoint2D 的 == 和 != 運算子 class CPoint2D { public: friend bool operator== (const CPoint2D &, const CPoint2D &); friend bool operator!= (const CPoint2D &, const CPoint2D &); … }; bool operator== (const CPoint2D &p1, const CPoint2D &p2) { return (p1._x == p2._x && p1._y == p2._y); } bool operator!= (const CPoint2D &p1, const CPoint2D &p2) return (p1._x != p2._x || p1._y != p2._y);

CString 的 == 和 != 運算子 class CString { public: friend bool operator== (const CString &cs1, const CString &cs2); friend bool operator== (const CString &cs, const char *s); friend bool operator== (const char *s, const CString &cs); friend bool operator!= (const CString &cs1, const CString &cs2); friend bool operator!= (const CString &cs, const char *s); friend bool operator!= (const char *s, const CString &cs); friend bool cs_equal (const char *, int, const char *, int); … };

// File: CString.cpp bool cs_equal (const char *s1, int len1, const char *s2, int len2) { if (len1 != len2) return false; else if (s1 == 0 || s2 == 0) return true; else return (strcmp(s1, s2) == 0); }

// File: CString.h inline bool operator== (const CString &cs1, const CString &cs2) { return cs_equal(cs1._string, cs1._size, cs2._string, cs2._size) } inline bool operator== (const CString &cs, const char *s) return cs_ equal(cs._string, cs._size, s, strlen(s)) inline bool operator== (const char *s, const CString &cs) return cs_ equal(s, strlen(s), cs._string, cs._size)

// File: CString.h inline bool operator!= (const CString &cs1, const CString &cs2) { return !cs_equal(cs1._string, cs1._size, cs2._string, cs2._size) } inline bool operator!= (const CString &cs, const char *s) return !cs_ equal(cs._string, cs._size, s, strlen(s)) inline bool operator!= (const char *s, const CString &cs)

Operator [] CPoint2D 的 [] 運算子 左邊的設計讓 [] 運算子可以用來存取 CPOint2D 物件的 x 座標值與 y 座標值。譬如: CPoint2D p(3,4); p[0] = p[0] + p[1]; p[1] = p[0] - p[1]; cout << p; 輸出的結果為:(7, 3) class CPoint2D { public: int& operator[] (int index) { assert(index == 0 || index == 1); return (index == 0) ? _x : _y; } … };

CString 的 [] 運算子 左邊的設計讓 [] 運算子可以用來存取 CString 物件的個別字元值。譬如: class CString { public: char& operator[] (int index) { assert(index >= 0 || index < _size); return _string[index]; } … }; 左邊的設計讓 [] 運算子可以用來存取 CString 物件的個別字元值。譬如: CString s(“howdy!”); s[2] = ‘x’; cout << s << s[0]; 輸出的結果為:hoxdy!h (假定 CString 支援 << 輸出運算子)

Operator () operator () 通常用來定義所謂的函式物件(function object)。譬如: class AbsInt { public: int operator () (int val) { return (val >= 0)? val : -val; } }; 則底下程式的輸出為:10 and 10 AbsInt a; cout << a(10) << “ and “ << a(-10);

User-Defined Conversions 如果有必要的話,我們可以為類別設計資料轉換的方式。假定 X 代表一個類別。X 單一參數的建構函式可視為一種資料轉換的函式,可用來把其它型態的資料轉換成 X 物件。譬如: class Y; class X { public: X (int); // 把 int 轉換成 X 物件 X (float); // 把 float 轉換成 X 物件 X (Y); // 把 Y 物件轉換成 X 物件 … };

反過來說,我們也可以用轉換運算子(conversion operator)把 X 類別的物件轉換成其它型態的物件。轉換運算子是 X 類別的一種成員函式,其格式如下: operator T (); 其中的 T 代表一個資料型態的名稱。這樣的轉換運算子是用來把 X 類別的物件轉換成 T 型態的物件。譬如: class Y; class X { public: operator int (); // 把 X 物件轉換成 int operator float (); // 把 X 物件轉換成 float operator Y (); // 把 X 物件轉換成 Y 物件 … };

轉換運算子 operator T () 必須滿足下列的條件: 轉換運算子必須是成員函式。 轉換運算子不可指定傳回值的型態。 轉換運算子不可有參數。 T 可以是 C++ 內建的基本資料型態、其它類別的名稱、或 typedef 的名稱。 T 不可以是陣列型態或函式型態。

operator int (X); // error: 不是成員函式 typedef int[100] iarray; typedef (*int)() funcptr; class X { public: int operator int (); // error: 不可有傳回值型態 operator int (int); // error: 不可有參數 operator iarray (); // error: 不可是陣列型態 operator funcptr (); // error: 不可是函式型態 // … };

範例