c++筆記05---雙目運算符,單目操作符,下標操作符,函數操作符,解引用
1. 雙目複合運算符 1
+= / -= / *=
左值,左操作數的引用;
左變右不變
(a += b) = c;
這裏 a 得到 c 的值,b 沒起作用;
下面這個例子實現上面這個效果:
Complex{
public:
Complex (int r = 0, int i = 0):m_r(r), m_i(i){}
void print(void) const { // 輸出
cout << m_r << '+' << m_r << 'i' << endl;
}
Comples& opeartor+= (const Complex& r) {
m_r += r.m_r;
m_i += r.m_i;
return *this; // 因爲返回的是引用,所以這裏加星
}
// 用友元實現 -= 操作,這裏用了 frined,所以雖然在 Complex 類裏面,但是是全局函數
friend Complex& operator-= (Complex& l, const Complex& r) {
l.m_r -= r.m_r;
l.m_i -= r.m_i;
return l; // 返回左值,給c1
}
private:
int m_r;
int m_i;
};
int main(){
Complex c1(1, 2), c2(3, 4);
c1 += c2; // 相當於 c1.operator += (c2);
c1.print();
Complex c3(5, 6);
(c1 += c2) = c3;
c1.print(); // 5+6i
c1 -= c2; // 相當於 ::operator-= (c1, c2);
c1.print(); // (5+6i) - (3+4i) = 2+2i;
(c1 += c2) = c3;
c1.print(); // 5+6i
return 0;
}
操作符重載-成員函數形式:AA& opeartor+= (const AA&);
操作符重載-友員函數形式:friend AA& operator-= (AA&, const AA&);
2. 雙目運算符 2
<< / >>
左操作數類型爲:ostream/istream,不能是常量,不能拷貝;
右操作數爲自定義類型;對於輸入,右操作數不能是常量;輸出可以;
表達式的值是左操作數的引用;
cout << c << i << endl; 相當於下面表達式:
cout.operator<< (c).operator<< (i).operator<< (endl);
一般用全局/友員方式實現:
::operator<< (cout, c).operator<< (i).operator<< (endl);
因爲輸入輸出是 c++ 標準庫裏的類函數,不能自己寫,所以不能重載成成員函數;
Complex{
public:
Complex (int r = 0, int i = 0):m_r(r), m_i(i){}
void print(void) const { // 輸出
cout << m_r << '+' << m_r << 'i' << endl;
}
friend ostream& operator<< (ostream& os, const Complex& r) // 輸出,const爲了支持常量型操作數
// cout 是私有的,不能拷貝構造,所以給 os 加引用,不創造新對象;
// operator 返回的是當前對象 cout,所以返回值應該加引用;
// 這個函數只是需要打印,不需要創造新對象,所以最後 r 也加引用;既然加了引用,就加 const,可以傳常量進來;
{ return os << r.m_r << '+' << r.m_i << 'i'; }
friend istream& operator>> (istream& is, Complex& r) // 輸入
{ return is >> r.m_r >> r.m_i; }
private:
int m_r; // 實步
int m_i; // 虛步
};
int main(){
Complex c1(1, 2), c2(3, 4);
cout << c1 << endl << c2 << endl;
//::operator<<(::operator<<(cout,c1)).operator<<((endl),c2).operator<<(endl);
cin >> c1 >> c2;
c1.print();
c2.print();
return 0;
}
3. 單目運算符 1
-(取負),!(非),~(反)
操作數不變,表達式的值是右值;
Complex{
public:
Complex (int r = 0, int i = 0):m_r(r), m_i(i){}
void print(void) const { // 輸出
cout << m_r << '+' << m_r << 'i' << endl;
}
const Complex operator-(void) const {
return Complex (-m_r, -m_i);
}
friend const Complex operator~ (const Complex& o){
return Complex (o.m_i, o.m_r); // 實步虛步交換
}
private:
int m_r;
int m_i;
};
int main(){
Complex c1(1, 2);
Complex c2 = -c1; // c2 = c1.opeartor-();
c2.print(); // -1+-2i
Complex c3 = ~c1; // 這裏的~不一定是按位取反,可以自定義,現在實現操作數交換 c3 = ::operator~(c1);
c3.print(); // 2+1i
return 0;
}
4. 單目運算符 2
前++,前--;
操作數發生改變,表達式爲左值,運算以後的值;就是操作數的引用;
(++i) = 100; // i = 100;
++++++i; // i = 103;
Complex{
public:
Complex (int r = 0, int i = 0):m_r(r), m_i(i) {}
void print(void) const { // 輸出
cout << m_r << '+' << m_r << 'i' << endl;
}
Complex& operator++ (void){ // 單目無須參數
// 返回左值,所以返回&,也就是左值本身,可以修改;如果沒有&,返回的是副本;
++m_r;
++m_i;
return *this; // 返回自引用
}
friend Complex& operator-- (complex& o){
--o.m_r;
--o.m_i;
return o;
}
private:
int m_r;
int m_i;
};
int main(){
Complex c1(1, 2);
Complex c2 = ++c1; // c2 = c1.operator++();
c1.print(); // 2+3i
c2.print(); // 2+3i
(++c1) = Complex (10, 20);
c1.print(); // 10+20i
(++++++c1).print(); // 13+23i
c2 = --c1; // c2 = c1.operator--();
c1.print(); // 12+22i
c2.print(); // 12+22i
return 0;
}
5. 單目運算符 3
後++,後--
操作數變,表達式的值是右值,運算前的值;
(i++) = 100; // error
i++++++; // error
Complex{
public:
Complex (int r = 0, int i = 0):m_r(r), m_i(i){}
void print(void) const { // 輸出
cout << m_r << '+' << m_r << 'i' << endl;
}
const Complex operator++ (int) { // 啞元
Complex old(*this);
++m_r;
++m_i;
return old; // 返回加之前的值
}
friend const Complex operator-- (Complex& o, int){ // 啞元
Complex old(o);
--m_r;
--m_i;
return old;
}
private:
int m_r;
int m_i;
};
int main(){
Complex c1(1, 2);
Complex c2 = c1++; // c2 = c1.operator++(0); 這裏帶參數只是爲了區分前++,參數無用
c1.print(); // 2+3i
c2.print(); // 1+2i
(c1++) = c2; // error
c2 = c1--; // c2 = ::operator--(c1, 0);
c1.print(); // 1+2i
c2.print(); // 2+3i
return 0;
}
備註:a++++; 錯誤,返回爲 const 型;
++++a; 可以,返回爲引用;
6. 其他操作符
下標操作符 [ ],類似於數組:
int arr[10] = {...};
cout << arr[1] << endl;
下標表達式是雙目運算符;
左操作數是一個具有容器特性的對象,右操作數是容器中特定數據元素的索引(基零下標);
下標表達式的值可以是左值,也可以是右值,由容器對象的常屬性決定;
常容器下標表達式的值是右值;反之,非常容器下標表達式爲左值;
class Array {...}; // 用於有容器特性的類
Array arr (...); // 容器對象,無常屬性,可以修改
arr[1] = 10; // 當左值
cout << arr[1] << endl; // 當右值
class Array {
public:
Array (size_t size = 1) : m_data (new int[size]) {}
~Array (void) {
if (m_data) {
delete m_data;
m_data = NULL; // 把 m_data 置空,防止重複釋放出錯;
}
}
int& operator[] (size_t i) { return m_data[i]; }
const int& operator[] (size_t i) const { return m_data[i]; }
/*
上面兩條語句冗餘了,兩個 return 一樣,可以寫成下面這樣:
const int& operator[] (size_t i) const {
return const_cast<Array&>(*this)[i];} // 不能直接返回 (*this)[i]
int operator[] (size_t i) const { return m_data[i]; } // 直接返回值也可以
*/
private:
int* m_data;
};
int main(void) {
Array arr (10); // 因爲返回左值,所以這裏的容器 arr 就不能是 const
for (size_t i = 0; i < 10; ++i)
arr[i] = i; // arr.operator[](i) = i; 返回第 i 個元素的引用
arr[0]++; // ok
const Array& cr = arr; // 返回右值
for (size_t i = 0; i < 10; ++i)
cout << cr[i] << ' ';
cr[0]++; // error,因爲 cr 具有常屬性
return 0;
}
7. 其他操作符 2
函數操作符:()
如果爲一個類定義了形如:
返回類型 operator()(行參表){. . .}
那麼這個類所實例化的對象可以當函數使用;
class Square {
double operator() (double x) {
return x * x;
}
};
class Integer {
public:
Integer (int i = 0) : m_i (i) {}
void print () { cout << m_i << endl; }
Integer& operator() (int i) {
m_i += i;
return *this;
}
Integer& operator, (int i) { // 逗號運算符
m_i += i;
return *this;
}
private:
int m_i;
};
int main (void) {
Square square;
cout << square(3) << endl; // square.operator()(3);
Integer i (10);
i (1)(2)(3);
i.print(); // 10 + 1 + 2 + 3 = 16
i, 1, 2, 3;
i.print(); // 16 + 1 + 2 + 3 = 22
return 0;
}
8. 其他操作符 3
間接解引用( * )和間接訪問(->)操作符
以指針的方式使用類類型對象;
class A {
public:
A (void) {};
~A (void) {};
};
class PA {
public:
PA (A* p = NULL) : m_p (p) {}
~PA () {
if (m_p) {
delete m_p;
m_p = NULL;
}
}
A& operator* (void) const { // 爲了讓下面的 pa 可以用箭頭訪問
return *m_p;
}
A* operator-> (void) const {
return &**this; // return m_p;
}
void hello(void) {};
private:
A* m_p;
};
void foo () {
A* p = new A; // 不執行析構,因爲沒有 delete;p是棧對象
A a; // 棧對象,執行析構,因爲有下面那個花括號;
PA pa = new A; // PA自己delete,pa 是對象,和上面的p不一樣
pa -> hello(); // pa.operator->()->hello();
(*pa).hello(); // pa.operator*().hello();
PA pb = pa; // 淺拷貝,運行錯誤
}
int main () {
foo ();
return 0;
}
9. 其他操作符 4
new 和 delete 重載
#include <cstdlib>
class A {
public:
static void* operator new (size_t size) { // 不同平臺,size_t 自動換爲不同類型
void* p1 = malloc (size); // 動態分配內存給 p
cout << size << p1 << endl; // size = 16
return p1; // 返回內存地址
}
static void operator delete (void* p1){
free (p1);
}
static void* operator new[] (size_t size){
void* p2 = malloc (size); // size = 32
return p2;
}
static void operator delete[] (void* p2){
free(p2);
}
private:
int m_i;
double m_d;
char m_c;
};
int main(){
cout << sizeof(A) << endl; // IIIIDDDDDDDDCXXX, 16字節
A* pa = new A;
cout << pa << endl; // pa 地址和上面 p1 地址一樣
pa = new A[2];
cout << pa << endl; // pa 地址和上面 p2 地址一樣
delete[] pa;
return 0;
}
操作符重載小結:
只能被重載成成員函數的運算符:
= += -= *= /= [] () ->
只能被重載成全局函數的運算符:
<< >>
10. 自定義類型轉換
通過單參構造實現自定義類型轉換;
如果在 A 類中有一個可以接受 B 類對象爲唯一參數的構造函數;
那麼 B 類型的對象就可以根據構造函數轉換爲 A 類型;
class Integer {
public:
Integer (int i = 0) : m_i (i) {} // 通過構造函數實現隱式轉換;
void print() { cout << m_i << endl; }
Integer& operator() (int i) {
m_i += i;
return *this;
}
Integer& operator, (int i) { // 逗號運算符
m_i += i;
return *this;
}
private:
int m_i;
};
Integer bar() {
return 300; // return 和函數返回值不一樣,也會自動隱式轉換;
}
int main (void) {
Integer i(10);
i = 100; // 100爲常量,i爲Integer類型,編譯器通過上面 Integer(int) 把右邊轉爲左邊類型
i.print();
bar().print();
}
通過 explicit 可以強制構造函數使用顯示轉換;
如果寫成: explicit Integer (int i = 0) : m_i (i) {}
那麼下面 main 函數裏的 i 也要顯示轉換:i = (Integer)(100);
11. 通過類型轉換操作符函數實現自定義類型轉換:
operator B (void) const {...}
那麼 A 類對象就可以轉換爲 B 類對象;
上面例子可以把 Integer 轉換爲 int,需要在 public 裏面加上如下函數:
operator int (void) const {
return m_i;
}
main 函數裏面加上如下代碼:
int n = i;
cout << n << endl;
舉例:如果 s 是 string 類型
如下代碼:const char* c = s; 會報錯,需要加上如下代碼:
operator const char*() const { return s; }
12. 自定義類型轉換總結:
如果目標類型是類類型,源類型是基本類型;
那麼就只能通過在目標類型中,定義以源類型爲單參的構造函數,實現類型轉換;
如果目標類型是基本類型,源類型是類類型;
那麼就只能通過在源類型中定義以目標類型爲函數名的類型轉換操作符函數實現類型轉換;
如果目標類型和源類型都是類類型,那麼以上兩種方法任取其一;但是不能同時使用;
如果目標類型和源類型都是基本類型,那麼就無法實現自定義類型轉換;
13. 操作符重載的一些限制:
1)要實現操作符重載,至少有一個是類對象;
int a = 10, b = 23;
int c = a + b;
int operator+(int a, int b) {
return a * b;
}
// ERROR! 兩個操作數都是普通變量,所以無法用這個加法函數實現乘法運算;
2)下面的操作符不可以重載:
:: 作用域限定
. 直接成員訪問
.* 直接成員指針解引用(間接的可以)
? : 三目運算符
sizeof 獲取字節數(注意:sizeof 是運算符,不是函數)
typeid 獲取類型信息
3)下面的操作符不可以用全局函數的方式實現,只能用成員函數的方式實現:
= 拷貝賦值
[ ] 下標
( ) 函數
-> 間接成員訪問
如果操作符既能用全局,也能用成員,我們首先選擇成員,限定其作用域,提高安全性;
4)不能自己發明新的運算符:
x ** y; // error, 沒有乘乘運算符;
14. 練習:實現 3 * 3 矩陣
支持 + / += / * / *= / 前++ / 後-- / <<
15. 練習:實現一個 Date 類,實現一下功能:
+ / += :增加 n 天后是哪一天
- / -= :減去 n 天后是哪一天
- :計算兩個日期間隔
>> :讀取,例如 2013 5 24
<< :輸出,例如 2013-5-24
+= / -= / *=
左值,左操作數的引用;
左變右不變
(a += b) = c;
這裏 a 得到 c 的值,b 沒起作用;
下面這個例子實現上面這個效果:
Complex{
public:
Complex (int r = 0, int i = 0):m_r(r), m_i(i){}
void print(void) const { // 輸出
cout << m_r << '+' << m_r << 'i' << endl;
}
Comples& opeartor+= (const Complex& r) {
m_r += r.m_r;
m_i += r.m_i;
return *this; // 因爲返回的是引用,所以這裏加星
}
// 用友元實現 -= 操作,這裏用了 frined,所以雖然在 Complex 類裏面,但是是全局函數
friend Complex& operator-= (Complex& l, const Complex& r) {
l.m_r -= r.m_r;
l.m_i -= r.m_i;
return l; // 返回左值,給c1
}
private:
int m_r;
int m_i;
};
int main(){
Complex c1(1, 2), c2(3, 4);
c1 += c2; // 相當於 c1.operator += (c2);
c1.print();
Complex c3(5, 6);
(c1 += c2) = c3;
c1.print(); // 5+6i
c1 -= c2; // 相當於 ::operator-= (c1, c2);
c1.print(); // (5+6i) - (3+4i) = 2+2i;
(c1 += c2) = c3;
c1.print(); // 5+6i
return 0;
}
操作符重載-成員函數形式:AA& opeartor+= (const AA&);
操作符重載-友員函數形式:friend AA& operator-= (AA&, const AA&);
2. 雙目運算符 2
<< / >>
左操作數類型爲:ostream/istream,不能是常量,不能拷貝;
右操作數爲自定義類型;對於輸入,右操作數不能是常量;輸出可以;
表達式的值是左操作數的引用;
cout << c << i << endl; 相當於下面表達式:
cout.operator<< (c).operator<< (i).operator<< (endl);
一般用全局/友員方式實現:
::operator<< (cout, c).operator<< (i).operator<< (endl);
因爲輸入輸出是 c++ 標準庫裏的類函數,不能自己寫,所以不能重載成成員函數;
Complex{
public:
Complex (int r = 0, int i = 0):m_r(r), m_i(i){}
void print(void) const { // 輸出
cout << m_r << '+' << m_r << 'i' << endl;
}
friend ostream& operator<< (ostream& os, const Complex& r) // 輸出,const爲了支持常量型操作數
// cout 是私有的,不能拷貝構造,所以給 os 加引用,不創造新對象;
// operator 返回的是當前對象 cout,所以返回值應該加引用;
// 這個函數只是需要打印,不需要創造新對象,所以最後 r 也加引用;既然加了引用,就加 const,可以傳常量進來;
{ return os << r.m_r << '+' << r.m_i << 'i'; }
friend istream& operator>> (istream& is, Complex& r) // 輸入
{ return is >> r.m_r >> r.m_i; }
private:
int m_r; // 實步
int m_i; // 虛步
};
int main(){
Complex c1(1, 2), c2(3, 4);
cout << c1 << endl << c2 << endl;
//::operator<<(::operator<<(cout,c1)).operator<<((endl),c2).operator<<(endl);
cin >> c1 >> c2;
c1.print();
c2.print();
return 0;
}
3. 單目運算符 1
-(取負),!(非),~(反)
操作數不變,表達式的值是右值;
Complex{
public:
Complex (int r = 0, int i = 0):m_r(r), m_i(i){}
void print(void) const { // 輸出
cout << m_r << '+' << m_r << 'i' << endl;
}
const Complex operator-(void) const {
return Complex (-m_r, -m_i);
}
friend const Complex operator~ (const Complex& o){
return Complex (o.m_i, o.m_r); // 實步虛步交換
}
private:
int m_r;
int m_i;
};
int main(){
Complex c1(1, 2);
Complex c2 = -c1; // c2 = c1.opeartor-();
c2.print(); // -1+-2i
Complex c3 = ~c1; // 這裏的~不一定是按位取反,可以自定義,現在實現操作數交換 c3 = ::operator~(c1);
c3.print(); // 2+1i
return 0;
}
4. 單目運算符 2
前++,前--;
操作數發生改變,表達式爲左值,運算以後的值;就是操作數的引用;
(++i) = 100; // i = 100;
++++++i; // i = 103;
Complex{
public:
Complex (int r = 0, int i = 0):m_r(r), m_i(i) {}
void print(void) const { // 輸出
cout << m_r << '+' << m_r << 'i' << endl;
}
Complex& operator++ (void){ // 單目無須參數
// 返回左值,所以返回&,也就是左值本身,可以修改;如果沒有&,返回的是副本;
++m_r;
++m_i;
return *this; // 返回自引用
}
friend Complex& operator-- (complex& o){
--o.m_r;
--o.m_i;
return o;
}
private:
int m_r;
int m_i;
};
int main(){
Complex c1(1, 2);
Complex c2 = ++c1; // c2 = c1.operator++();
c1.print(); // 2+3i
c2.print(); // 2+3i
(++c1) = Complex (10, 20);
c1.print(); // 10+20i
(++++++c1).print(); // 13+23i
c2 = --c1; // c2 = c1.operator--();
c1.print(); // 12+22i
c2.print(); // 12+22i
return 0;
}
5. 單目運算符 3
後++,後--
操作數變,表達式的值是右值,運算前的值;
(i++) = 100; // error
i++++++; // error
Complex{
public:
Complex (int r = 0, int i = 0):m_r(r), m_i(i){}
void print(void) const { // 輸出
cout << m_r << '+' << m_r << 'i' << endl;
}
const Complex operator++ (int) { // 啞元
Complex old(*this);
++m_r;
++m_i;
return old; // 返回加之前的值
}
friend const Complex operator-- (Complex& o, int){ // 啞元
Complex old(o);
--m_r;
--m_i;
return old;
}
private:
int m_r;
int m_i;
};
int main(){
Complex c1(1, 2);
Complex c2 = c1++; // c2 = c1.operator++(0); 這裏帶參數只是爲了區分前++,參數無用
c1.print(); // 2+3i
c2.print(); // 1+2i
(c1++) = c2; // error
c2 = c1--; // c2 = ::operator--(c1, 0);
c1.print(); // 1+2i
c2.print(); // 2+3i
return 0;
}
備註:a++++; 錯誤,返回爲 const 型;
++++a; 可以,返回爲引用;
6. 其他操作符
下標操作符 [ ],類似於數組:
int arr[10] = {...};
cout << arr[1] << endl;
下標表達式是雙目運算符;
左操作數是一個具有容器特性的對象,右操作數是容器中特定數據元素的索引(基零下標);
下標表達式的值可以是左值,也可以是右值,由容器對象的常屬性決定;
常容器下標表達式的值是右值;反之,非常容器下標表達式爲左值;
class Array {...}; // 用於有容器特性的類
Array arr (...); // 容器對象,無常屬性,可以修改
arr[1] = 10; // 當左值
cout << arr[1] << endl; // 當右值
class Array {
public:
Array (size_t size = 1) : m_data (new int[size]) {}
~Array (void) {
if (m_data) {
delete m_data;
m_data = NULL; // 把 m_data 置空,防止重複釋放出錯;
}
}
int& operator[] (size_t i) { return m_data[i]; }
const int& operator[] (size_t i) const { return m_data[i]; }
/*
上面兩條語句冗餘了,兩個 return 一樣,可以寫成下面這樣:
const int& operator[] (size_t i) const {
return const_cast<Array&>(*this)[i];} // 不能直接返回 (*this)[i]
int operator[] (size_t i) const { return m_data[i]; } // 直接返回值也可以
*/
private:
int* m_data;
};
int main(void) {
Array arr (10); // 因爲返回左值,所以這裏的容器 arr 就不能是 const
for (size_t i = 0; i < 10; ++i)
arr[i] = i; // arr.operator[](i) = i; 返回第 i 個元素的引用
arr[0]++; // ok
const Array& cr = arr; // 返回右值
for (size_t i = 0; i < 10; ++i)
cout << cr[i] << ' ';
cr[0]++; // error,因爲 cr 具有常屬性
return 0;
}
7. 其他操作符 2
函數操作符:()
如果爲一個類定義了形如:
返回類型 operator()(行參表){. . .}
那麼這個類所實例化的對象可以當函數使用;
class Square {
double operator() (double x) {
return x * x;
}
};
class Integer {
public:
Integer (int i = 0) : m_i (i) {}
void print () { cout << m_i << endl; }
Integer& operator() (int i) {
m_i += i;
return *this;
}
Integer& operator, (int i) { // 逗號運算符
m_i += i;
return *this;
}
private:
int m_i;
};
int main (void) {
Square square;
cout << square(3) << endl; // square.operator()(3);
Integer i (10);
i (1)(2)(3);
i.print(); // 10 + 1 + 2 + 3 = 16
i, 1, 2, 3;
i.print(); // 16 + 1 + 2 + 3 = 22
return 0;
}
8. 其他操作符 3
間接解引用( * )和間接訪問(->)操作符
以指針的方式使用類類型對象;
class A {
public:
A (void) {};
~A (void) {};
};
class PA {
public:
PA (A* p = NULL) : m_p (p) {}
~PA () {
if (m_p) {
delete m_p;
m_p = NULL;
}
}
A& operator* (void) const { // 爲了讓下面的 pa 可以用箭頭訪問
return *m_p;
}
A* operator-> (void) const {
return &**this; // return m_p;
}
void hello(void) {};
private:
A* m_p;
};
void foo () {
A* p = new A; // 不執行析構,因爲沒有 delete;p是棧對象
A a; // 棧對象,執行析構,因爲有下面那個花括號;
PA pa = new A; // PA自己delete,pa 是對象,和上面的p不一樣
pa -> hello(); // pa.operator->()->hello();
(*pa).hello(); // pa.operator*().hello();
PA pb = pa; // 淺拷貝,運行錯誤
}
int main () {
foo ();
return 0;
}
9. 其他操作符 4
new 和 delete 重載
#include <cstdlib>
class A {
public:
static void* operator new (size_t size) { // 不同平臺,size_t 自動換爲不同類型
void* p1 = malloc (size); // 動態分配內存給 p
cout << size << p1 << endl; // size = 16
return p1; // 返回內存地址
}
static void operator delete (void* p1){
free (p1);
}
static void* operator new[] (size_t size){
void* p2 = malloc (size); // size = 32
return p2;
}
static void operator delete[] (void* p2){
free(p2);
}
private:
int m_i;
double m_d;
char m_c;
};
int main(){
cout << sizeof(A) << endl; // IIIIDDDDDDDDCXXX, 16字節
A* pa = new A;
cout << pa << endl; // pa 地址和上面 p1 地址一樣
pa = new A[2];
cout << pa << endl; // pa 地址和上面 p2 地址一樣
delete[] pa;
return 0;
}
操作符重載小結:
只能被重載成成員函數的運算符:
= += -= *= /= [] () ->
只能被重載成全局函數的運算符:
<< >>
10. 自定義類型轉換
通過單參構造實現自定義類型轉換;
如果在 A 類中有一個可以接受 B 類對象爲唯一參數的構造函數;
那麼 B 類型的對象就可以根據構造函數轉換爲 A 類型;
class Integer {
public:
Integer (int i = 0) : m_i (i) {} // 通過構造函數實現隱式轉換;
void print() { cout << m_i << endl; }
Integer& operator() (int i) {
m_i += i;
return *this;
}
Integer& operator, (int i) { // 逗號運算符
m_i += i;
return *this;
}
private:
int m_i;
};
Integer bar() {
return 300; // return 和函數返回值不一樣,也會自動隱式轉換;
}
int main (void) {
Integer i(10);
i = 100; // 100爲常量,i爲Integer類型,編譯器通過上面 Integer(int) 把右邊轉爲左邊類型
i.print();
bar().print();
}
通過 explicit 可以強制構造函數使用顯示轉換;
如果寫成: explicit Integer (int i = 0) : m_i (i) {}
那麼下面 main 函數裏的 i 也要顯示轉換:i = (Integer)(100);
11. 通過類型轉換操作符函數實現自定義類型轉換:
operator B (void) const {...}
那麼 A 類對象就可以轉換爲 B 類對象;
上面例子可以把 Integer 轉換爲 int,需要在 public 裏面加上如下函數:
operator int (void) const {
return m_i;
}
main 函數裏面加上如下代碼:
int n = i;
cout << n << endl;
舉例:如果 s 是 string 類型
如下代碼:const char* c = s; 會報錯,需要加上如下代碼:
operator const char*() const { return s; }
12. 自定義類型轉換總結:
如果目標類型是類類型,源類型是基本類型;
那麼就只能通過在目標類型中,定義以源類型爲單參的構造函數,實現類型轉換;
如果目標類型是基本類型,源類型是類類型;
那麼就只能通過在源類型中定義以目標類型爲函數名的類型轉換操作符函數實現類型轉換;
如果目標類型和源類型都是類類型,那麼以上兩種方法任取其一;但是不能同時使用;
如果目標類型和源類型都是基本類型,那麼就無法實現自定義類型轉換;
13. 操作符重載的一些限制:
1)要實現操作符重載,至少有一個是類對象;
int a = 10, b = 23;
int c = a + b;
int operator+(int a, int b) {
return a * b;
}
// ERROR! 兩個操作數都是普通變量,所以無法用這個加法函數實現乘法運算;
2)下面的操作符不可以重載:
:: 作用域限定
. 直接成員訪問
.* 直接成員指針解引用(間接的可以)
? : 三目運算符
sizeof 獲取字節數(注意:sizeof 是運算符,不是函數)
typeid 獲取類型信息
3)下面的操作符不可以用全局函數的方式實現,只能用成員函數的方式實現:
= 拷貝賦值
[ ] 下標
( ) 函數
-> 間接成員訪問
如果操作符既能用全局,也能用成員,我們首先選擇成員,限定其作用域,提高安全性;
4)不能自己發明新的運算符:
x ** y; // error, 沒有乘乘運算符;
14. 練習:實現 3 * 3 矩陣
支持 + / += / * / *= / 前++ / 後-- / <<
15. 練習:實現一個 Date 類,實現一下功能:
+ / += :增加 n 天后是哪一天
- / -= :減去 n 天后是哪一天
- :計算兩個日期間隔
>> :讀取,例如 2013 5 24
<< :輸出,例如 2013-5-24
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.