1.加号运算符重载
作用:实现两个自定义数据类型相加的运算
如果自己自定义一个加号,很麻烦
代码(this在哪个成员函数中,则下面调用这个函数时this->m_a指的是这个函数所属类的那个成员属性。比如下面代码中的this->m_a,在test()函数调用personadd()时,值得是person类中m_b的值)
class person
{
public:
int m_a;
int m_b = 20;
person personplus(person &p)
{
person temp;
temp.m_a = this->m_a + p.m_a;//这里的this->m_b必须在public中赋值才能用,不然乱码
return temp; //临时对象,存信息
}
};
//全局函数重载,不能用this了
person personadd(person &p1, person &p2)
{
person temp;
temp.m_a = p1.m_a + p2.m_b;
temp.m_b = p1.m_b + p2.m_b;
cout << temp.m_a << endl;
cout << temp.m_b << endl;
return temp;
}
void test()
{
person p1;
p1.m_a = 10;
person p2;
p2.m_a = 33;
p2.m_b = 20;
person p3;
p3.personadd(p1,p2);
person p4 = p1.personplus(p2); //temp存着这个对象所有的信息,再赋给p4,等于p4 = p1 +p2,加号重载
cout << "新 "<< p4.m_a << endl;
}
int main()
{
test();
system("pause");
return 0;
}
而编译器提供的函数名字operator+,可以将person p3 = p1.personplus(p2)简化为p3 = p1+p2,
- 成员函数重载,本质是person p3 = p2.operator+(p1),p1.p2的先后顺序是有很大影响
- 全局函数重载也是一样,本质上是p3 = operator+(p1,p2),p1.p2的先后顺序是有很大影响
- 运算符重载也可以函数重载,实现如person + int 这种操作
class person
{
public:
int m_a;
int m_b ;
//成员函数重载
//person operator+(person &p) //可以写成p,但用引用的方法节省内存空间
//{
// person temp;
// temp.m_a = this->m_a + p.m_a;
// temp.m_b = this->m_b + this->m_b + p.m_b;
// return temp; //注意返回一个对象,这个对象包含各属性的值
//}
};
//全局函数重载
person operator+(person &p1,person &p2)
{
person temp;
temp.m_a = p1.m_a + p1.m_a + p2.m_a;
temp.m_b = p1.m_b + p2.m_b;
return temp;
}
//函数重载版本
person operator+(person &p1, int num) //此处不能&num,因为没有别名,就是原始名
{
person temp;
temp.m_a = p1.m_a + p1.m_a + num;
temp.m_b = p1.m_b + num;
return temp;
}
void test()
{
person p1;
p1.m_a = 10;
p1.m_b = 23;
person p2;
p2.m_a = 33;
p2.m_b = 20;
person p3; person p4;
//成员函数重载p3结果
p3 = p2 + p1; //注意:成员函数重载,这里简化的是person p3 = p2.operator+(p1),p1.p2的先后顺序是有很大影响的。
// p1+p2 = 66,p2 + p1 =63
//全局函数重载p4结果
p4 = p2 + p1; //全局函数重载也是一样,本质上是p3 = operator+(p1,p2)
//因为这里设置的是p1.m_a + p1.m_a + p2.m_a,设计的全局重载函数先后位置顺序有影响。
//运算符重载也可以函数重载
person p5 = p1 + 10; //person + int,需要自己写一个重载函数
cout << p3.m_b << endl;
cout << p4.m_a << endl;
cout << p5.m_a << endl;
}
int main()
{
test();
system("pause");
return 0;
}
注意:运算符重载不能改变元尤类型的运算,如把1+1 =2,变成1 +1 = 0
2.左移运算符重载
想输出自定义类型变量,必须左移运算符重载
如cout << p << endl;
注意:成员函数无法实现左移运算符重载,因为要使用成员函数必须用对象p来调用,则此时p已经在左边。会形成 p << cout 这种形式。(cout是一个ostream类的对象,它有一个成员运算符函数operator<<,每次调用的时候就会向 输出设备(一般就是屏幕)输出)
如void operator<<(cout) 在调用时为p.operator<<(cout) ,可简化为p << cout,简化规则为去掉.operator和(),此时变成了p << cout,反了
具体实现(注意注释)
- 左移重载函数参数和返回值的类型最好一致,所以代码中都为ostream &,都为一个地址
- 左移重载函数只有在符合形参条件下才会生效。以代码为例,只有cout << person类型时,才调用左移重载函数。在其他情况下,比如调用完重载函数,返回一个cout,右边为<< "你好"这种情况,<<依然为普通的<<,不会调用重载函数
class person
{
friend ostream & operator<<(ostream &out, person &p); //因为有些类把属性设为私有
person()
{
}
public: //构造函数要说明公共还是私有
person(int a,int b)
{
m_a = a;
m_b = b;
}
private:
int m_a;
int m_b ;
/*void operator<<(ostream &cout) //成员函数,调用时相当于把p.operator<<(cout) 简化成了p << cout,p在左边,不符合
{}*/
};
//所以左移运算符重载不能用成员函数
ostream & operator<<(ostream &out,person &p) //这里相当于p.operator<<(cout,p),简化为cout << p.
//cout是ostream 类的一个对象,且全局只能有一个,所以加&,用引用符号,起别名,所以叫out也行
{
out << "m_a = " << p.m_a << " m_b = " << p.m_b << endl;
return cout; //这里ostream后加&,是为了使cout这个对象 地址保持不变,返回一个地址,而不是新建一个新地址的cout
} //也可以这样理解,返回值和参数类型必须统一,参数为引用,则返回值也为引用,可能理解不深,以后再改
void test()
{
person p1(10,23);
//如果上面的左移重载函数返回值为空(void)
//cout << p1 ; //则这里<<右边必须都是person类型,不能写endl,会报错
//如果如果上面的左移重载函数返回值为cout
cout << "hello " << p1 << endl;
//则可以继续用链式思想,返回一个cout,变成cout << endl;
//这里没有调用左移重载函数,因为只有一个cout, << 右边是endl,不符合左移重载函数的形参要求,所以<<是普通的那个<<,右边可以加endl换行
//中间也可以加个hello
}
int main()
{
test();
system("pause");
return 0;
}
3.递增递减重置运算符
重点:
- /重载前置++运算符要加&,不加&返回的只是一个值,地址会随着操作不断改变。而加&后,返回的是一个固定地址的,随着操作不断改变的值。地址不变
- 重载后置运算符不加&,返回的只是一个值。因为临时存量temp在执行后就会被释放掉,不需要固定地址(我的理解,不知道对不对,以后再修改)。
- 此时的左移重载运算符自定义变量形参处,或是不加&,或者是const+&。因为后置运算返回的是一个值,每回的值相同,但地址都可能不同,所以不加&或其他来满足前置后置共同要求,不再要求固定地址。或者用const+&后,地址不变,也可以满足
//自定义整型
class myinteger
{
friend ostream & operator<<(ostream &cout, myinteger const &m);
public:
myinteger()
{
m_num = 0;
}
//重载前置++运算符
myinteger& operator++() //因为只是对参数递增,且为成员函数,所以不需要传参
//这里必须加&,不加&返回的只是一个值,再对m1进行操作时,每次的地址都不同
{ //就是拷贝调用原则
++m_num;
return *this; //this代表自身地址,*为解引用,这里返回一个自定义整型的固定地址的解引用值
}
//重载后置++运算符
//int代表一个占位参数,用于区分前后置。只能写int.编译器会自动认定为后置++
myinteger operator++(int) //后值递增必须返回一个值,不能返回引用
{
//先记录当时结果
myinteger temp = *this;
//再递增并返回当时结果
m_num++;
return temp; //这里返回值返回的地址就是
}
private:
int m_num;
};
ostream & operator<<(ostream &cout, myinteger const &m)//因为后置运算返回的是一个值,每回的值相同,但地址都可能不同
//所以这里的myinteger m不能加&。以此满足前置后置共同的需求
//也可以const 再加&,静态化
{
cout << "数字 " << m.m_num << endl;
return cout;
}
void test()
{
myinteger m1;
//前置
cout << "hello " << ++(++m1) << endl;
cout << m1 << endl; //前置重载函数不加&时,m1为1,因为再次递增时已经是另外一个地址的m1递增。
//后置
cout << m1++ << endl; //这两个地址不同
cout << m1 << endl;
}
int main()
{
test();
system("pause");
return 0;
}