函数类型 operator 运算符(参数表);
函数类型 类名::operator 运算符(参数表)
{
函数体;
}
friend 函数类型 operator 运算符(参数表);
函数类型 operator 运算符(参数表)
{
函数体;
}
返回值 operator运算符(参数列表){}
重载运算符时,函数声明在类内和类外是有区别的,比方说 + - * / 等需要2个操作数的运算符,当声明在类的外部时,则参数列表为2个参数,第一个参数为运算符左边的操作数,而第二个参数为操作符右边的操作数:如下
classType operator+(classType& left, classType& right);
而当函数声明在类的内部时,即为类的成员函数时,
classType operator+(classType& right );
而第一个操作数就是调用该操作符的对象的引用,第二个操作数是传进来的参数,所以,如果要重载<<运算符,一般写法是这样的
ostream& operator<<(ostream& os, const classType& obj);
则第一个参数是该运算符的第一个操作数,然而,却不是类对象,
所以当该类运算符重载时写在类的内部时,又为了访问类内除public外的其它变量或函数,
则应当声明为友元函数:
friend ostream& operator<<(ostream& os, const classType& obj);
所以,为什么有些运算符的重载要声明为友元函数,是类似的。
运算符重载原则表:
运算符 | 建议使用方式 |
---|---|
一元运算符 | 成员函数 |
= ( ) [ ] -> | 必须是成员函数 |
+= -= /= *= ^= &= != %= >>= <<= | 成员函数 |
所有其它二元运算符, 例如: –,+,*,/ | 友元函数 |
<< >> | 友元函数 |
参数:
当参数不会被改变,一般按const引用来传递(若是使用成员函数重载,函数也为const).
返回数值:
定义格式
<函数类型> operator <运算符>(<参数表>)
{
<函数体>
}
双目运算符(或称二元运算符)是C++中最常用的运算符。双目运算符有两个操作数,通常在运算符的左右两侧,
+,-,*,/,%,<,>,>=,<=,==,!=,<<,>>,&,^,|,&&,||,=
如3+5,a=b,i<10等
注意:
双目运算符重载为类的成员函数时,函数只显式说明一个参数,该形参是运算符的右操作数,因为成员函数用this指针隐式地访问了类的一个对象,它充当了运算符函数最左边的操作数。
<对象名>.operator <运算符>(<参数>) //obj1.operator<运算符>(OBJ obj2)
等价于
<对象名><运算符><参数> //obj1<运算符>obj2
例如 a+b 等价于 a.operator+(b)
单目运算符是指运算所需变量为一个的运算符,即在运算当中只有一个操作数,又叫一元运算符。
逻辑非运算符:!、按位取反运算符:~、自增自减运算符:++, - - 等
<运算符> obj
相当于
obj.operator<运算符>();
例如:
++a 相当于 a.operator++();
obj <运算符>
相当于
obj.operator<运算符>(int);
例如:
a++ 相当于 a.operator++(int);
通常我们都将其声明为友元函数,因为大多数时候重载运算符要访问类的私有数据,如果不声明为类的友元函数,而是通过在此函数中调用类的公有函数来访问私有数据会降低性能。所以一般都会设置为类的友元函数,这样我们就可以在此非成员函数中访问类中的数据了。
声明的格式:
friend 函数类型 operator 运算符(参数表);
定义的格式:
friend 函数类型 operator 运算符(参数表){
函数体;
}
obj1 运算符 obj2 等同于 operator 运算符(obj1,obj2 )
运算符 obj 等同于 operator 运算符(obj )
obj 运算符 等同于 operator 运算符(obj,0 )
举例
//前置++运算符重载
friend 函数类型 & operator++(类类型 &);
//后置++运算符重载
friend 函数类型 & operator++(类类型 &,int);
//https://blog.csdn.net/zhuzhaoming1994/article/details/80371779
class Point
{
private:
int x;
public:
Point(int x1)
{ x=x1;}
Point(Point& p)
{ x=p.x;}
const Point operator+(const Point& p);//使用成员函数重载加号运算符
friend const Point operator-(const Point& p1,const Point& p2);//使用友元函数重载减号运算符
};
const Point Point::operator+(const Point& p)
{
return Point(x+p.x);
}
const Point operator-(const Point& p1,const Point& p2)
{
return Point(p1.x-p2.x);
}
结果:
Point a(1);
Point b(2);
a+b; //正确,调用成员函数
a-b; //正确,调用友元函数
a+1; //正确,先调用类型转换函数,把1变成类类型,之后调用成员函数
a-1; //正确,先调用类型转换函数,把1变成类类型,之后调用友元函数
1+a; //错误,调用成员函数时,第一个操作数必须是对象,因为第一个操作数还有调用成员函数的功能
1-a; //正确,先类型转换 后调用友元函数
//https://blog.csdn.net/zhuzhaoming1994/article/details/80371779
class Point
{
private:
int x;
public:
Point(int x1)
{ x=x1;}
Point& operator++();//成员函数定义自增
/*函数返回临时变量,不能出现在等号的左边(临时变量不能做左值),函数的结果只能做右值,则要返回一个const类型的值*/
const Point operator++(int x); //后缀可以返回一个const类型的值,这里引入一个虚参数int x,x可以是任意整数。
friend Point operator--(Point& p);//友元函数定义--
friend const Point operator--(Point& p,int x);//后缀可以返回一个const类型的值
};
Point Point::operator++()//++obj
{
x++;
return *this;
}
const Point Point::operator++(int x)//obj++
{
Point temp = *this;
this->x++;
return temp;
}
Point operator--(Point& p)//--obj
{
p.x--;
return p;
//前缀形式(--obj)重载的时候没有虚参,通过引用返回*this 或 自身引用,也就是返回变化之后的数值
}
const Point operator--(Point& p,int x)//obj--
{
Point temp = p;
p.x--;
return temp;
// 后缀形式obj--重载的时候有一个int类型的虚参, 返回原状态的拷贝
}
Point a(1);
Point b(2);
a++;//隐式调用成员函数operator++(0),后缀表达式
++a;//隐式调用成员函数operator++(),前缀表达式
b--;//隐式调用友元函数operator--(0),后缀表达式
--b;//隐式调用友元函数operator--(),前缀表达式
cout<<a.operator ++(2);//显式调用成员函数operator ++(2),后缀表达式
cout<<a.operator ++();//显式调用成员函数operator ++(),前缀表达式
cout<<operator --(b,2);//显式调用友元函数operator --(2),后缀表达式
cout<<operator --(b);//显式调用友元函数operator --(),前缀表达式
class Point
{
private:
int x;
public:
Point(int x1)
{ x=x1;}
friend ostream& operator<<(ostream& cout,const Point& p);//使用友元函数重载<<输出运算符
friend istream& operator>>(istream& cin,Point& p);//使用友元函数重载>>输出运算符
};
ostream& operator<<(ostream& cout,const Point& p)
{
cout<<p.x<<endl;
return cout;
}
istream& operator>>(istream& cin,Point& p)
{
cin>>p.x;
return cin;
}
语法:
重载方式:只能使用友元函数重载 且 使用三个引用&
函数名:
输出流: operator<<(参数表)
输入流:operator>>(参数表)
参数表:两个参数均用引用&
输出流: 必须是两个参数:对输出流ostream& 和 对象
第一个操作数cout,定义在文件iostream中,是标准类类型ostream的对象的引用。
如:ostream& cout,const Point& p
输入流:必须是两个参数:对输入流ostream& 和 对象
第一个操作数是cin,定义在文件iostream,实际上是标准类类型istream的对象的引用
如:instream& cin,const Point& p
函数调用:
输出流: 显式调用:cout<<对象
隐式调用: operator<<(cout,对象)
输入流:显式调用:cin>>对象
隐式调用: operator>>(cin,对象)
返回类型:返回类型固定 + 使用返回函数引用(满足连续输出)
输出流: 返回ostream&
如:ostream& operator<<(ostream& cout,const Point& p)
输入流:返回:istream&
如:istream& operator>>(istream& cin,Point& p)
类型转换函数的作用是将一个类的对象转换成另一类型的数据。(C++中没有返回类型的函数有3个,构造函数、析构函数、类型转换函数。)
operator 类型名( )
{
实现转换的语句
}
举例:
类型转换运算符,只要你把XXX对象隐式或者显式转换为T对象时,它都会被自动调用。
//https://www.cnblogs.com/renyuan/p/6555172.html
#include
using namespace std;
//类型转换运算符重载,只要你把XXX对象隐式或者显式转换为T对象时,它自动被调用
template<class T>
class Transfer
{
public:
Transfer(int arg):i(arg){}
operator T() const
{
return i;
}
private:
int i;
};
int main()
{
Transfer<double> t(3);
//double d =static_cast(t);//显示转换
double d = t;//隐式转换
cout<<d;
getchar();
return 0;
}
类成员访问运算符( -> )可以被重载,它被定义用于为一个类赋予"指针"行为。运算符 -> 必须是一个成员函数。如果使用了 -> 运算符,返回类型必须是指针或者是类的对象。
#include
using namespace std;
class DBHelper//动态分配内存更加方便
{
public:
DBHelper()
{
cout << " DBHelper()...." << endl;
}
~DBHelper()
{
cout << " ~DBHelper()" << endl;
//Close();
}
void Open()
{
cout << " Open()...." << endl;
}
void Query()
{
cout << " Query()...." << endl;
}
void Close()
{
cout << " Close()...." << endl;
}
};
class DB
{
public:
DB()
{
db_ = new DBHelper;//利用类对象确定性析构的特性
}
~DB()
{
delete db_;//利用类对象确定性析构的特性,将所包装的对象销毁掉
}
DBHelper* operator->()//重载指针运算符,返回的是DBHelper对象
{
return db_;
}
private:
DBHelper* db_;
};
int main()
{
DB db;
db->Open();
db->Query();
db->Close();
return 0;
}
String.h
#ifndef _STRING_H_
#define _STRING_H_
#include
using namespace std;
class String
{
public:
String(const char* str="");
String(const String& other);
String& operator=(const String& other);
String& operator=(const char* str);
bool operator!() const;
char& operator[](unsigned int index);
const char& operator[](unsigned int index) const;
friend String operator+(const String& s1, const String& s2);
//String operator+(const String& s1, const String& s2);
String& operator+=(const String& other);
friend ostream& operator<<(ostream& os, const String& str);
friend istream& operator>>(istream& is, String& str);
~String(void);
void Display() const;
private:
String& Assign(const char* str);
char* AllocAndCpy(const char* str);
char* str_;
};
#endif // _STRING_H_
String.cpp
#pragma warning(disable:4996)
#include "String.h"
#include
//#include
//using namespace std;
String::String(const char* str)
{
str_ = AllocAndCpy(str);
}
String::String(const String& other)
{
str_ = AllocAndCpy(other.str_);
}
String& String::operator=(const String& other)
{
if (this == &other)
return *this;
/*delete[] str_;
str_ = AllocAndCpy(other.str_);
return *this;*/
return Assign(other.str_);
}
String& String::operator=(const char* str)
{
/*delete[] str_;
str_ = AllocAndCpy(str);
return *this;*/
return Assign(str);
}
String& String::Assign(const char* str)
{
delete[] str_;
str_ = AllocAndCpy(str);
return *this;
}
char& String::operator[](unsigned int index)
{
return const_cast<char&>(static_cast<const String&>(*this)[index]);
//return str_[index];
}
const char& String::operator[](unsigned int index) const
{
return str_[index];
}
bool String::operator!() const
{
return strlen(str_) != 0;
}
char* String::AllocAndCpy(const char* str)
{
int len = strlen(str) + 1;
char* newstr = new char[len];
memset(newstr, 0, len);
strcpy(newstr, str);
return newstr;
}
String operator+(const String& s1, const String& s2)
{
//int len = strlen(s1.str_) + strlen(s2.str_) + 1;
//char* newchar = new char[len];
//memset(newchar, 0, len);
//strcpy(newchar, s1.str_);
//strcat(newchar, s2.str_);
//String tmp(newchar);//return String(newchar);内部构造函数分配空间,newchar没办法销毁
//delete newchar;
//return tmp;
String str = s1;
str += s2;
return str;
}
String& String::operator+=(const String& other)
{
int len = strlen(str_) + strlen(other.str_) + 1;
char* newchar = new char[len];
memset(newchar, 0, len);
strcpy(newchar, str_);
strcat(newchar, other.str_);
delete str_;
str_ = newchar;
return *this;
}
//cout 类型就是ostream,第一个参数是ostream,不是对象自身,所以选择友元方式重载
//返回值还是ostream&,保证接下来的对象还能够被继续输出,
//例如 cout << str << endl;//cout << str返回值还是ostream,所以可以继续输出<< endl
ostream& operator<<(ostream& os, const String& str)
{
os << str.str_;
return os;
}
istream& operator >> (istream& is, String& str)
{
char tmp[1024];
cin >> tmp;
str = tmp;
return is;
}
void String::Display() const
{
cout << str_ << endl;
}
String::~String()
{
delete[] str_;
}
main.cpp
#include "String.h"
//#include
//
//using namespace std;
int main()
{
String s1("abcdefg");
//s1.Display();
char ch = s1[2];
//cout << ch << endl;
s1[2] = 'A';
//s1.Display();
const String s2("abcdefgh");
//s2[2] = 'C';
//s2.Display();
String s3 = "xxx";//String(const char* str="");前面不能加explicit,需要隐式转化
String s4 = "yyy";
String s5 = s3 + s4;
s5.Display();
//如果以函数方式重载,不允许,因为成员函数隐含的一个参数是对象自身,不能够以"aaa"任意字符串开头
String s6 = "aaa" + s3;//不予许"bbb" + "aaa".因为String operator+(const String& s1, const String& s2)。
s6.Display();
s3 += s4;
s3.Display();
cout << s3 << endl;
String s7;
cin >> s7;
cout << s7 << endl;
return 0;
}
1、https://blog.csdn.net/zgl_dm/article/details/1767201
2、https://blog.csdn.net/insistGoGo/article/details/6626952
3、https://blog.csdn.net/zhuzhaoming1994/article/details/80371779
4、https://www.cnblogs.com/xiaokang01/p/9166745.html
5、https://www.cnblogs.com/renyuan/p/6555172.html