运算符的重载
1.运算符重载的两种方法:
类成员函数运算符重载
-
return_type class_name::operator op(operand2) {}
-
重载二元运算符时,成员运算符函数只需显式传递一个参数(即二元运算符的右操作数), 而左操作数则是该类对象本身,通过 this 指针隐式传递。
-
重载一元运算符时,成员运算符函数没有参数,操作数是该类对象本身,通过 this 指针隐式 传递
友元函数运算符重载
-
return_type operator op(operand1, operand2) {}
友元函数运算符
重载原则:
-
函数的形参代表依自左至右次序排列的各操作数。
-
参数个数=原操作数个数(后置++、后置--除外)
-
至少应该有一个自定义类型的参数。
-
后置单目运算符 ++和–-的重载函数,形参列表中要增加一个int,但不必写形参名。
-
如果在运算符的重载函数中需要操作某类对象的私有成员,可以将此函数声明为该类的友元。
2.可以重载的运算符
3、不可以重载的运算符
注意:
operator重载运算符时
不能覆盖原有的运算
, 即操作数中必须
至少有一个是
自建类型
, 这虽然限制了一点操作性, 但保护了程序的正常执行.
4.重载自增运算符时需注意:
• 若为前缀自增运算符,直接重载(以++举例):
• return_type class_name::operator ++() {}
• 若为后缀自增运算符,该函数有一个 int 类型的虚拟形参,这个形参在函数的主体中是不会被使
用的,这只是一个约定,它告诉编译器递增运算符正在后缀模式下被重载:
• return_type class_name::operator ++(int) {}
5.赋值运算符
class
Integer
{
int
x;
public
:
Integer(
int
x
=0):x(
x
) {
}
Integer
operator +
(
const
Integer
&
Int
) {
return
Integer
(x+
Int
.x);
}
Integer
operator -
(
const
Integer
&
Int
) {
return
Integer
(x-
Int
.x);
}
Integer
operator -
() {
return
Integer
(-x);
}
void
print() {
cout
<<
x
<<
endl;
}
};
int
main() {
Integer
a=3,b=4;
a.print();
b.print();
Integer
c=a
+
b;
c.print();
Integer
d=a
-
b;
d.print();
Integer
e=
-
a;
e.print();
//d = a + 4;正确,
当形参为const Integer/ const Integer&/
Integer
时,4作为参数传进来,开辟空间,调用了构造函数,创建了对象,发生了隐式转换。
//e = 4 + a;error,第一次操作数不是对象,不能发生隐式转换。
return
0;
}
5.重载原则
(1) 除了类属关系运算符"."、成员指针运算符".*"、作用域运算符"::"、sizeof运算符和三目运算符"?:"以外,C++中的所有运算符都可以重载。
(2) 重载运算符限制在C++语言中已有的运算符范围内的允许重载的运算符之中,
不能创建新的运算符
。
(3) 运算符重载实质上是函数重载,因此编译程序对运算符重载的选择,遵循
函数重载
的选择原则。
(4) 重载之后的运算符不能改变运算符的优先级和结合性,也不能改变运算符操作数的个数及语法结构。
(5) 运算符重载不能改变该运算符用于内部类型对象的含义。它只能和用户自定义类型的对象一起使用,或者用于用户自定义类型的对象和内部类型的对象混合使用时。
(6) 运算时,有数和对象的混合运算时,必须使用友元
(7) =,(),[], ->不能以友元方式重载
(8) 二元运算符中,第一个操作数为非对象时,必须使用友元函数。如输入输出运算符<<和>>
为什么输出运算符重载不能是一个成员函数?而非得声明为友元?
-
如果要重载<<运算符,一般写法是这样的
-
friend ostream& operator<<(ostream& os, const classType& obj);
-
friend istream& operator<<(istream& os, const classType& obj);
-
则第一个参数是该运算符的第一个操作数,然而,却不是该类对象,
-
所以当该类运算符重载时写在类的内部时,又为了访问类内除public外的其它变量或函数,
-
则应当声明为友元函数:
-
friend ostream& operator<<(ostream& os, const classType& obj);
6.重载输入输出操作符<< >>实例
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;
}
7、类型转换运算符重载
转换构造函数(包括拷贝构造函数)
-
普通类型到类类型的转换
转换构造函数(特殊的构造函数)
满足下列条件的构造函数称为转换构造函数:
1.
一个参数
的构造函数(或者除了
第一个
参数外其余参数都有默认值的多参构造函数,当然啦,第一个参数可以有默认值,也可以没有默认值)
2. 参数是基本类型
3. 参数是
其它
类类型
class
Integer
{
int
x;
public
:
Integer(
int
x
= 0) :x(
x
) {}
friend
Integer
operator+
(
const
Integer
&
lhs
,
const
Integer
&
rhs
) {
return
lhs
.x +
rhs
.x;
//① int->Integer
}
friend
ostream
&
operator<<
(
ostream
&
o
,
const
Integer
&
hs
) {
o
<<
hs
.x;
return
o
;
}
};
int
main() {
string
s;
s
=
"Hello"
;
//②const char* ->string
cout
<<
s
<<
endl;
Integer
i1(3), i2;
i2
=
1.1
+
i1;
//③double ->int int->Integer所以1.1+i1相当于operator+(
Integer(1),
i1),调用了转换构造函数
cout
<<
i2
<<
endl;
}
隐式转换——重载协议(const)
如果重载的函数参数一样,可以通过转换到某
个重载函数,编译会选择哪个版本?
(1)类型直接匹配的优先选择;
(2)const 类型实参匹配 const 版本
(3)非 const 实参优先匹配非 const 版本,
没有则隐式转换为 const 版本匹配。
class
Integer
{
int
x;
public
:
Integer(
int
x
= 0) :x(
x
) {}
friend
Integer
operator+
(
const
Integer
&
lhs
,
Integer
rhs
) {
return
lhs
.x +
rhs
.x;
}
friend
Integer
operator+
(
const
Integer
&
lhs
,
Integer
*
rhs
) {
if
(
rhs
)
return
lhs
.x +
rhs
->x + 2000;
else
return
lhs
.x + 1000;
}
friend
ostream
&
operator<<
(
ostream
&
o
,
const
Integer
&
hs
) {
o
<<
hs
.x;
return
o
;
}
};
class
Func
{
/* ... */
};
void
func(
Func
*
p
) { cout
<<
"pointer"
<<
endl; };
void
func(
int
a
) { cout
<<
"int"
<<
endl; };
int
main() {
func(
nullptr
);
// 输出:pointer
func(
NULL
);
// 输出:int
Integer
i1(1), i2;
i2
=
i1
+
&i1;
// 尝试 1,nullptr,NULL,&i1 取代 0
//0输出1001
//1输出2
//nullptr输出1001
//NULL输出1001
//&i1输出2002
cout
<<
i2
<<
endl;
}
explicit
C++提供了关键字explicit,可以
阻止不应该允许的经过转换构造函数进行的隐式转换的发生,声明为explicit的构造函数不能在隐式转换中使用。
C++中,
一个参数的构造函数(或者除了
第一个参数外其余参数都有默认值的多参构造函数), 承担了两个角色。
1 是个构造;2 是
个默认且隐含的类型转换操作符。
所以, 有时候在我们写下如 AAA = XXX, 这样的代码, 且恰好XXX的类型正好是AAA单参数构造器的参数类型, 这时候编译器就自动调用这个构造器, 创建一个AAA的对象。
这样看起来好象很酷, 很方便。 但在某些情况下, 却违背了程序员的本意。 这时候就要在这个构造器前面加上explicit修饰, 指定这个构造器只能被明确的调用/使用, 不能作为类型转换操作符被隐含的使用。
类型转换函数
1. C++类中可以定义类型转换函数
-
类型转换函数用于将类对象转换为其它类型。
-
函数原型为: operator Type(){} ;Type表示 类类型转换成的其他类型,即返回的类型
2. 类型转换函数与转换构造函数具有同等的地位
3. 使得编译器有能力将对象转化为其它类型
4. 编译器能够隐式的使用类型转换函数
-
类类型到普通类型的转换
class
Test
{
int
mValue;
public
:
Test(
int
i
= 0)
{
mValue =
i
;
}
int
value()
{
return
mValue;
}
operator
double
()
{
return
mValue;
}
};
int
main()
{
Test
t(100);
double
i;
i=t;//等效于double i=t;
//表示将t中的mValue转换成double类型返回,赋值给i
cout
<<
"t.value() = "
<<
t.value()
<<
endl;
cout
<<
"i = "
<<
i
<<
endl;
return
0;
}
-
类类型之间的转换
#include <iostream>
#include <string>
using namespace std;
class Value
{
public:
Value()
{
}
Value(Test& t)
{
cout << "explicit Value(Test& t)" << endl;
}
};
class Test
{
int mValue;
public:
Test(int i = 0)
{
mValue = i;
}
int value()
{
return mValue;
}
operator Value()
{
Value ret;
cout << "operator Value()" << endl;
return ret;
}
};
int main()
{
Test t(100);
Value v = t;
return 0;
}
从输出结果我们可以发现:转换构造函数和类型转换函数发生冲突了,编译器
不知道应该调用哪个函数
。因此发生了错误。
当然我们可以使用explicit关键字抑制隐式的转换构造函数,让程序只调用类型转换函数。但是,
我们无法抑制隐式的类型转换函数
。
补充:
static_cast是一个强制类型转换操作符。强制类型转换,也称为显式转换
8、类成员访问运算符 -> 重载
类成员访问运算符( -> )可以被重载,但它较为麻烦。它被定义用于为一个类赋予"指针"行为。运算符 -> 必须是一个成员函数。如果使用了 -> 运算符,返回类型必须是指针或者是类的对象。
运算符 -> 通常与指针引用运算符 * 结合使用,用于实现"智能指针"的功能。这些指针是行为与正常指针相似的对象,唯一不同的是,当您通过指针访问对象时,它们会执行其他的任务。比如,当指针销毁时,或者当指针指向另一个对象时,会自动删除对象。
间接引用运算符 -> 可被定义为一个一元后缀运算符。也就是说,给出一个类:
class Ptr{
//...
X * operator->();};
类 Ptr 的对象可用于访问类 X 的成员,使用方式与指针的用法十分相似。例如:
void
f
(
Ptr
p
)
{
p
->
m
=
10
;
// (p.operator->())->m = 10
}
语句 p->m 被解释为 (p.operator->())->m。同样地,下面的实例演示了如何重载类成员访问运算符 ->。
class
A
{
public
:
int
a;
A(
int
a
=37):a(
a
) { }
void
print() { cout
<<
a
<<
endl; }
};
class
B
{
public
:
int
b;
B() { b = 11; };
operator
A
() {
return
A
(b);
}
A
*
operator->
() {
A
* a =
new
A
;
*a
=
*
this
;
return
a;
}
};
int
main() {
A
a;
B
b;
b
->
print();
return
0;
}
9、赋值运算符重载
class
T
{
private
:
int
* mArray;
int
size;
public
:
T&
operator
=(
const
T& other)
{
if
(
this
!= &other)//期待检查自复制
{
if
(other.size != size)//
{
delete
[]mArray;
size = 0;
mArray =
nullptr
;
mArray =
new
int
[other.size];
size = other.size;
}
std::copy(other.mArray, other.mArray + other.size, mArray);
}
return
*
this
;
}
};
copy(被复制对象首地址/begin(),被复制对象尾地址/end(),复制对象的首地址/begin())
//declaring & initializing an int array
int arr[] = { 10, 20, 30, 40, 50 };
//向量声明
vector<int> v1(5);
//复制数组元素到向量
copy(arr, arr + 5, v1.begin());
Output:
//如果我们打印值
arr: 10 20 30 40 50
v1: 10 20 30 40 50