Classes (2) Lecture 7
Basic concepts Overloaded operators are functions with special names: the keyword operator followed by the symbol for the operator being defined. 3 + 4 + 3 4 3 4 + operator+ (3, 4) conventional notation 5 * (3 + 4) * 5 + 3 4 5 3 4 + * operator* (5, operator+ (3, 4)) Polish notation reverse Polish notation functions
Basic concepts [RECALL] Overloaded functions are functions that have the same name but different parameter lists and that appear in the same scope are overloaded. (Lecture 5) [NOTE] An operator function must either be a member of a class or have at least one parameter of class type. implicit this pointer It is illegal to (re-)define operators for built-in types.
Recall operator<< class Sales_item { public: friend ostream& operator<< (ostream &out, const Sales_item &item) { out << item.isbn << “, $“ << item.revenue << “, “ << item.units_sold; return out; } private: string isbn; double revenue; unsigned int units_sold; };
Overloading operator>> cannot be const class Sales_item { public: friend istream& operator>> (istream &in, Sales_item &item) { in >> item.isbn >> item.revenue >> item.units_sold; return in; } }; must be friend Sales_item item; cin >> item; cout << item; 978-0-321-71411-4 569.9 10 978-0-321-71411-4, $569.9, 10
File IO Sales_item item; ifstream fin(“data.txt”); Tested on macOS High Sierra ver. 10.13.6 with GCC ver. 4.2.1 [Nov 22, 2018] File IO Sales_item item; ifstream fin(“data.txt”); if (!fin.is_open( )) return 1; ofstream iout(“items.txt”); ofstream pout(“prices.txt”); while (!fin.eof( )) { fin >> item; iout << item << endl; pout << item.get_isbn( ) << “: $” << item.avg_price( ) << endl; } fin.close( ); iout.close( ); pout.close( ); return 0; 打开输入文件 判断是否打开成功 data.txt 978-0-321-71411-4 569.9 10 978-0-262-03384-8 523.68 8 输出文件1,打印 输出文件2,均价 使用重载的>> items.txt 使用重载的<< 978-0-321-71411-4, $569.9, 10 978-0-262-03384-8, $523.68, 8 prices.txt 978-0-321-71411-4: $56.99 978-0-262-03384-8: $65.46 关闭文件
Overloading arithmetic operators 返回caller object的引用 参数通常是const引用 Sales_item& operator+= (const Sales_item &item) { assert( this->isbn == item.isbn ); this->revenue += item.revenue; this->units_sold += item.units_sold; return *this; } 实现为类的成员函数
Overloading arithmetic operators The following overloaded operator is also legal (if it really makes some sense). 返回caller object的引用 Sales_item& operator+= (unsigned int n) { assert( this->units_sold != 0 ); this->revenue += this->avg_price( ) * n; this->units_sold += n; return *this; } 实现为类的成员函数
Overloading arithmetic operators 返回一个新的object 参数应当是const引用 Sales_item operator+ (const Sales_item &item1, const Sales_item &item2) { assert( item1.isbn == item2.isbn ); Sales_item result; result.isbn = item1.isbn; result.revenue = item1.revenue + item2.revenue; result.units_sold = item1.units_sold + item2.units_sold; return result; } 通常需要是类的友元 对应的类要允许复制/有良定义的复制 局部变量result以复制的方式返回
Never go counterintuitive Sales_item operator+ (const Sales_item &item1, const Sales_item &item2) { Sales_item result; result.isbn = item1.isbn; result.revenue = max(item1.revenue, item2.revenue); result.units_sold = max(item1.units_sold, item2.units_sold); return result; } DON’T DO THIS.
Overloading the assignment operator 返回caller object的引用 参数应当是const引用 Sales_item& operator= (const Sales_item &item) { this->isbn = item.isbn; this->revenue = item.revenue; this->units_sold = item.units_sold; return *this; } 需要是类的成员函数 [RECALL] Copy constructors.
Non-copyable objects Sometimes there is the case where we want to forbid an object from being copied. class Sales_item { public: Sale_item( ); Sales_item(const Sales_item&) = delete; Sales_item& operator= (const Sales_item&) = delete; }; 必须显式声明/定义一个默认构造函数 声明拷贝构造函数和拷贝赋值操作符为deleted Sales_item item1; Sales_item item2(item1); Sales_item item3 = item1; Sales_item item1; Sales_item item2; ok. 可以定义两个object error. 找不到拷贝构造函数或拷贝赋值操作符
Singletons In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one object.
static Singleton* getInstance( ) { Tested on macOS High Sierra ver. 10.13.6 with GCC ver. 4.2.1 [Nov 22, 2018] class Singleton { public: static Singleton* getInstance( ) { if (instance == nullptr) instance = new Singleton( ); return instance; } static void destroyInstance( ) { delete instance; } int data; private: Singleton( ): data(2) { } Singleton(const Singleton&) = delete; Singleton& operator= (const Singleton&) = delete; static Singleton *instance; }; Singleton* Singleton::instance(nullptr); 声明构造函数为private,禁任意创建 声明拷贝构造函数和拷贝赋值操作符为deleted,禁复制 用静态变量instance和相应的静态函数管理唯一对象
error. 构造函数为private error. 构造函数为private ok. ok. 指针p和s指向相同的(唯一)对象 Singleton s; error. 构造函数为private Singleton *s = new Singleton( ); error. 构造函数为private Singleton *s = Singleton::getInstance( ); ok. Singleton *p = Singleton::getInstance( ); ok. 指针p和s指向相同的(唯一)对象 p->data = 7; cout << s->data << ”, ” << p->data; 7, 7 Singleton::destroyInstance( ); ok. 销毁(唯一)对象 Tested on macOS High Sierra ver. 10.13.6 with GCC ver. 4.2.1 [Nov 22, 2018]
Overloading relational operators bool operator== (const Sales_item &item1, const Sales_item &item2) { return item1.isbn == item2.isbn && fabs(item1.revenue – item2.revenue) < 1e-7 && item1.units_sold == item2.units_sold; } 可以定义其他的operator==逻辑 bool operator!= (const Sales_item &item1, const Sales_item &item2) { return !(item1 == item2); }
Overloading relational operators bool operator< (const Sales_item &item1, const Sales_item &item2) { return item1.isbn < item2.isbn; } Many STL functions/algorithms require that objects be comparable. vector<Sales_item> items; sort( items.begin( ), items.end( ) ); 要求Sales_item类定义了operator<,否则编译器报错。
Overloading the subscript operator class StrVector { public: StrVector(const string *arr, size_t n) { … } string& operator[ ] (size_t n) { return element[n]; } const string& operator[ ] (size_t n) const { return element[n]; } private: string *elements; }; version 1 version 2 string s[4] = {“hello”, “how”, “are”, “you”}; StrVector sv1(s, 4); sv1[0] = “hi”; const StrVector sv2(s, 4); cout << sv2[3]; Call version 1. Bounds checking? Call version 2.
Overloading member access operators Problems with pointers: ptr2 ptr3 需要记得delete; 需要清楚什么时候delete ptr1 object Sales_item *ptr1 = new Sales_item( ); Sales_item *ptr2 = ptr1; delete ptr1; delete ptr2; ok. The Sales_item object is deleted. run-time error. Pointer being freed was not allocated.
Smart pointers SomeClass *obj; size_t *count; 自定义SmartPointer类,额外使用一个reference counter,记录当前object被多少个smart pointer指向(即初始的smart pointer被复制了多少次) ptr1 ptr2 ptr3 reference counter object 3 SmartPointer(SomeClass*); SmartPointer(const SmartPointer&); ~SmartPointer( ); SmartPointer& operator= (const SmartPointer&); SomeClass& operator* ( ); SomeClass* operator-> ( );
创建一个新的SmartPointer对象,指向输入参数p所指向的SomeClass对象,同时创建计数器,且计数器的值为1 SomeClass *obj; size_t *count; 主要函数1:构造函数 SmartPointer(SomeClass *p): obj(p), count( new size_t(1) ) { } ptr1 ptr2 ptr3 reference counter object 3 创建一个新的SmartPointer对象,指向输入参数p所指向的SomeClass对象,同时创建计数器,且计数器的值为1
将SmartPointer对象sp复制为一个新的SmartPointer对象,计数器加1 SomeClass *obj; size_t *count; 主要函数2:拷贝构造函数 ptr1 ptr2 ptr3 reference counter object 3 SmartPointer(const SmartPointer &sp): obj(sp.obj), count(sp.count) { ++*count; } 将SmartPointer对象sp复制为一个新的SmartPointer对象,计数器加1
主要函数3:析构函数 计数器减1 如果计数器为0,执行实际的delete SomeClass *obj; size_t *count; ~SmartPointer( ) { if (--*count == 0) { delete obj; delete count; } ptr1 ptr2 ptr3 reference counter object 3 计数器减1 如果计数器为0,执行实际的delete
放弃原有的指向,计数器减1,必要时执行实际的delete 主要函数4:拷贝赋值操作符 SomeClass *obj; size_t *count; SmartPointer& operator= (const SmartPointer &sp) { if (this == &sp) return *this; if (--*count == 0) { delete obj; delete count; } obj = sp.obj; count = sp.count; ++*count; return *this; 防止self-assignment 放弃原有的指向,计数器减1,必要时执行实际的delete ptr1 ptr2 ptr3 reference counter object 3 复制sp,计数器加1
使得SmartPointer类的使用看起来与SomeClass类的指针一样。 SomeClass *obj; size_t *count; 主要函数5-6:成员访问操作符 ptr1 ptr2 ptr3 reference counter object 3 SomeClass& operator* ( ) { return *obj; } SomeClass* operator-> ( ) { return obj; } 使得SmartPointer类的使用看起来与SomeClass类的指针一样。
SomeClass *obj; size_t *count; int *data = new int(-29); SmartPointer sp1(data); cout << *sp1 << endl; cout << sp1.get_count() << endl; SmartPointer *sp2 = new SmartPointer(sp1); cout << **sp2 << endl; cout << sp2->get_count() << endl; SmartPointer sp3(*sp2); cout << *sp3 << endl; cout << sp3.get_count() << endl; delete sp2; SomeClass *obj; size_t *count; -29 1 ptr1 ptr2 ptr3 reference counter object 3 -29 2 -29 3 2
Smart pointers Smart pointer和singleton有什么区别? sp1和sp2有什么关系? SomeClass *obj; size_t *count; ptr1 ptr2 ptr3 reference counter object 3 [THINK] Smart pointer和singleton有什么区别? [THINK] int *data = new int(-29); SmartPointer sp1(data); SmartPointer sp2(data); sp1和sp2有什么关系?
Functors Functors (i.e., function objects) are classes that overload the call operator. Functors allow objects of its type to be used as if they were a function. struct absInt { int operator( )(int val) const { return val < 0 ? –val : val; } }; absInt obj; cout << obj(-7) << endl; cout << absInt( )(-7) << endl; 使用名为obj的absInt类对象的operator( ) 使用临时对象的operator( ),临时对象在语句结束后消亡
Functors class averageLessX { public: averageLessX(int n): val(n) { } bool operator( )(int v1, int v2) const { return (v1 + v2) / 2.0 < val; } private: int val; }; averageLessX avg_less_10(10); cout << avg_less_10(4, 16) << endl; cout << averageLessX(10)(4, 16) << endl; 使用名为avg_less_10的对象 使用临时对象averageLessX(10),临时对象在语句结束后消亡
Functors vs pointers to functions [RECALL] (Lecture 5) bool x_less (const Point &a, const Point &b); bool y_less (const Point &a, const Point &b); void mySort ( vector<Point> &points, bool (*p) (const Point &, const Point &) ) { // 用(*p)(a, b)来比较两个点a和b }
Functors vs pointers to functions struct xLess { bool operator( ) (const Point &p1, const Point &p2) const { return p1.x < p2.x; } }; void mySort (vector<Point> &points, const xLess &comp);
Functors vs pointers to functions Next lecture! struct myLess { virtual bool operator( ) (const Point &p1, const Point &p2) const = 0; }; struct xLess: public myLess { virtual bool operator( ) (const Point &p1, const Point &p2) const { return p1.x < p2.x; } }; struct yLess: public myLess { virtual bool operator( ) (const Point &p1, const Point &p2) const { return p1.y < p2.y; } }; void mySort (vector<Point> &points, const myLess &comp);
Next lecture C++ Primer, Chapter 15