運算符重載
一、友元機制
1.友元介紹
·友元是一種允許非類成員函數訪問類的非公有成員的一種機制
·可以把一個函數指定為類的友元,也可以把整個類指定為另一個類的友元。
友元函數
友元類
2.友元函數
·友元函數在類作用域外定義,但它需要在類體中進行說明
·為了與該類的成員函數加以區別,定義的方式是在類中用關鍵字friend說明該函數,格式如下:
friend 類型 友元函數名(參數表)
友元的作用在于提高程序的運行效率
友元函數的注意事項:
·友元函數不是類的成員函數,在函數體中訪問對象的成員,必須用對象名加運算符“.”加對象成員名。但友元函數可以訪問類中的所有成員(公有的、私有的、保護的),一般函數只能訪問類中的公有成員。
·友元函數不受類中的訪問權限關鍵字限制,可以把它放在類的公有、私有、保護部分,但結果一樣。
·某類的友元函數的作用域并非該類作用域。如果連友元函數不是另一類的成員函數,則其作用域為另一類的作用域,否則與一般函數相同。
·友元函數破壞了面向對象程序類的封裝性,所有友元函數如不是為了必須使用則盡可能少用?;蛘哂闷渌侄伪Wo封裝性。
3.友元類
友元類的注意事項:
·友元關系是單向的
·友元關系不能被傳遞
·友元關系不能被繼承
代碼示例:
String.h;#ifndef _STRING_H_#define _STRING_H_ class String{public: friend class StringTool;friend void PRint(const String &s1); //友元函數String();String(char *str);~String();void Display();private:char *str_; };#endifString.cpp:#include <iostream>#include "String.h" using namespace std; String::String(){cout << "default constructor String!" << endl;}String::String(char *str){cout << "constructor String" << endl;int len = strlen(str) + 1;str_ = new char(len);memset(str_, 0, len);strcpy(str_, str);} String :: ~String(){cout << "destory String" << endl;} void String::Display(){cout << str_ << endl;} void print(const String& s1)//友元函數{cout << s1.str_ << endl;} StringTool.h:#ifndef _STRINGTOOL_H_#define _STRINGTOOL_H_#include "String.h" class StringTool{public:void mystrcat(String& s1, String& s);}; #endifStringTool.cpp:#include <iostream>#include <string.h>#include "StringTool.h" void StringTool::mystrcat(String &s1, String &s){strcat(s1.str_, s.str_);} main.c:#include <iostream>#include "String.h"#include "StringTool.h"using namespace std; int main(){String s1("hello");print(s1);//String s2("world");//StringTool st;//st.mystrcat(s1, s2);//調用友元類 return 0;}運行結果:
二、運算符重載
1.運算符重載介紹
·運算符重載允許把標準運算符(如+、—、*、/、<、>等)應用于自定義數據類型的對象
運算符重載的作用:
·直觀自然,可以提高程序的可讀性
·體現了C++的可擴充性
·運算符重載僅僅只是為了語法上的方便,它是另一種函數調用方式
·運算符重載,本質上是函數重載
運算符重載的注意事項:
·不要濫用重載,本質上是函數重載
2.運算符重載的實現
(1)成員函數重載
· 成員函數原型格式:
函數類型 Operator 運算符(參數表);
·成員函數定義的格式:
函數類型 類名 :: operator 運算符(參數表)
{
函數體;
}
(2)友元函數重載
·友元函數原型的格式:
friend 函數類型 operator運算符(參數表);
·友元函數定義的格式:
函數類型 函數類型 類名::operator 運算符(參數表)
{
函數體;
}
3.運算符重載規則
·運算符重載不允許發明新的運算符
·不能改變運算符操作對象的個數
·運算符被重載后,其優先級和結合性不會改變
·不能被重載的運算符
作用域解析運算符 ::
條件運算符 ?:
直接成員訪問運算符 .
類成員指針引用的運算符 .*
sizeof運算符 sizeof
·成員函數重載和友元函數重載的選擇
一般情況下,單目運算符最好重載為類的成員函數;雙目運算符則最好重載為類的友元函
數
以下一些雙目運算符不能重載為類的友元函數:= ()、[]、->
類型轉換運算符只能以成員函數方式重載
流運算符只能以友元的方式重載
4.++運算符的重載
(1)成員函數重載方式
(2)友元函數重載方式
代碼示例:
Integer.h:#ifndef _INTEGER_H_#define _INTEGER_H_ class Integer{public: Integer(); Integer(int n); ~Integer(); void Display() const; Integer& operator++(); Integer operator++(int n); // friend Integer& operator++(Integer &i); //友元函數重載方式 // friend Integer operator++(Integer &i,int n); //友元函數重載方式 operator int();private: int n_;};#endif Integer.cpp:#include "Integer.h"#include <iostream> using namespace std; Integer :: Integer(){ } Integer :: Integer(int n) : n_(n){ } Integer :: ~Integer(){ } void Integer :: Display() const{ cout << n_ << endl;} Integer& Integer :: operator++(){ cout << "++i" << endl; ++n_; return *this;}Integer Integer :: operator++(int n){ cout << "i++" << endl; Integer tmp(n_); n_++; return tmp;} #if 0 Integer& operator++(Integer& i){ cout << "friend ++i" << endl; ++i.n_; return i;} Integer operator++(Integer& i,int n){ cout << "friend i++" << endl; Integer temp(i.n_); i.n_++; return temp;} #endif Integer :: operator int(){ return n_;} Main.c:#include "Integer.h"#include <iostream> using namespace std; Integer :: Integer(){ } Integer :: Integer(int n) : n_(n){ } Integer :: ~Integer(){ } void Integer :: Display() const{ cout << n_ << endl;} Integer& Integer :: operator++(){ cout << "++i" << endl; ++n_; return *this;}Integer Integer :: operator++(int n){ cout << "i++" << endl; Integer tmp(n_); n_++; return tmp;} #if 0 Integer& operator++(Integer& i){ cout << "friend ++i" << endl; ++i.n_; return i;} Integer operator++(Integer& i,int n){ cout << "friend i++" << endl; Integer temp(i.n_); i.n_++; return temp;} #endif Integer :: operator int(){ return n_;}運行結果請自行實踐~
5.!運算符重載
成員函數原型:
bool operator!() const;
成員函數定義:
bool String :: operator!() const
{
return strlen(str_) ! = 0;
}
6.賦值運算符重載
成員函數原型:
String& operator = (const String& other);
成員函數定義:
String& String :: operator = (const String& other)
{
if(this == &other)
{
return * this;
}
int len = strlen(other.str_) + 1;
delete[] str_;
str_ = new char[len];
memset(str_,0,len);
strcpy(str_,other.str_);
}
7.[]運算符重載
成員函數原型:
(1)char& operator[](unsigned int index);
(2)const char& operator[](unsigned int index) const;
成員函數定義:
(1)char& String :: operator[](unsigned int index)
{
cout << "no const" << endl;
//return str_[index];
return const_cast<char&>(static_cast<const String&>(*this)[index]);
}
(2)const char& String :: operator[](unsigned int index) const
{
cout << "const[]" << endl;
return str_[index];
}
8.+運算符重載
成員函數原型:
friend String operator+(const String &s1,const String &s2);
成員函數定義:
String operator+(const String& s1,const String& s2)
{
#if 0
int len = strlen(s1.str_) + strlen(s2.str_) + 1;
char * newstr = new char[len];
memset(newstr,0,len);
strcpy(newstr,s1.str_);
strcat(newstr,s2.str_);
String temp(newstr);
delete newstr;
#endif //實現+=后可以直接使用下面幾行代碼
String temp(s1);
temp += s2;
return temp;
}
9.+=運算符重載
成員函數原型:
String& operator+=(const String& other);
成員函數定義:
String& String :: operator+=(const String& other)
{
int len = strlen(str_) + strlen(other.str_) + 1;
char * newstr = new char[len];
memset(newstr,0,len);
strcpy(newstr,str_);
strcat(newstr,other.str_);
delete[] str_;
str_ = newstr;
return * this;
}
10.流運算符重載
·為什么一定要使用友元函數進行重載?
如果是重載雙目操作符(即為類的成員函數),就只要設置一個參數作為右側運算量,而左側運算量就是對象本身
而 >> 或<<左側運算量是 cin或cout而不是對象本身,所以不滿足后面一點,就只能申明為友元函數了
成員函數原型:
(1)friend ostream& operator<<(ostream& out,const String& s);
(2)friend istream& operator>>(istream& in,String& s);
成員函數定義:
(1)ostream& operator<<(ostream& out,const String& s)
{
out << s.str_;
return out;
}
(2)istream& operator>>(istream& in,String& s)
{
char buffer[1024];
in >> buffer;
int len = strlen(buffer) + 1;
delete[] s.str_;
s.str_ = new char[len];
strcpy(s.str_,buffer);
return in;
}
11.類型轉換運算符重載
·必須是成員函數,不能是友元函數
·沒有參數(操作數是什么?)
·不能指定返回類型(其實已經指定了)
·函數原型:operator 類型名();
Integer :: operator int()
{
return n_;
}
int main(void)
{
Integer n(100);
n = 200;
n.Display();
int sum = add(n,100);
cout << sum << endl;
int x = n;
int v = static cast<int>(n);
return 0;
}
12.->運算符重載
#include <iostream>
using namespace std;
class DBHelper
{
public:
DBHelper()
{
cout<<"DB ..."<<endl;
}
~DBHelper()
{
cout<<"~DB ..."<<endl;
}
void Open()
{
cout<<"Open ..."<<endl;
}
void Close()
{
cout<<"Close ..."<<endl;
}
void Query()
{
cout<<"Query ..."<<endl;
}
};
class DB
{
public:
DB()
{
db_ = new DBHelper;
}
~DB()
{
delete db_;
}
DBHelper* operator->()
{
return db_;
}
private:
DBHelper* db_;
};
int main(void)
{
DB db;
db->Open();
db->Query();
db->Close();
return 0;
}
13.new/delete
(1)operator new
(1) 只分配所要求的空間,不調用相關對象的構造函數。當無法滿足所要求分配的空間時,則 ->如果有new_handler,則調用new_handler,否則
->如果沒要求不拋出異常(以nothrow參數表達),則執行bad_alloc異常,否則
->返回0
(2) 可以被重載
(3) 重載時,返回類型必須聲明為void*
(4) 重載時,第一個參數類型必須為表達要求分配空間的大?。ㄗ止潱?,類型為size_t
(5) 重載時,可以帶其它參數
void * operator new(size_t size)
{
cout <<“void * operator new(size_t size size)” << endl;
void *p = malloc(size);
return p;
}
void operator delete(void *p)
{
cout <<“void operator delete(void *p)” << endl;
free(p);
}
(2)new operator
new operator
(1)調用operator new分配足夠的空間,并調用相關對象的構造函數
(2)不可以被重載
(3)placement new(不分配內存+構造函數的調用)
新聞熱點
疑難解答
圖片精選