递增运算符重载函数
#include<iostream>
#include<string>
using namespace std;
class myint
{
friend ostream& operator<<(ostream& cout,const myint& p);
private:
int m_num;
public:
myint()
{
m_num = 0;
}
myint& operator++()//前置运算符重载函数(引用传递)
{
m_num++;
return *this;
}
//myint operator++()//前置运算符重载函数(引用传递)
//{
// m_num++;
// return *this;
//}
myint operator++(int)//后置运算符重载函数
{
myint temp=*this;
m_num++;
return temp;
}
};
ostream& operator<<(ostream& cout,const myint& p)//注意:左移运算符重载尽量别定义用成员函数定义,要用全局函数去定义。
{
cout << p.m_num << endl;
return cout;
}
void test01()//前置运算符重载测试(返回值是引用类型的)
{
myint p1;
cout <<"++p= "<< ++p1 << endl;
cout << "运行完++p以后p1.m_num= " << p1 << endl;
cout << "++(++p1) " << ++(++p1) << endl;
cout << "运行完++(++p1)以后p1.m_num= " << p1 << endl;
}//运行完以后为什么++(++p1) 值为3,因为前面已经执行过一次++p了。
void test02()//前置运算符重载测试(返回不是引用而是对象)
{
myint p1;
cout << "++p= " << ++p1 << endl;
cout << "运行完++p以后p1.m_num= " << p1 << endl;
cout << "++(++p1) " << ++(++p1) << endl;
cout << "运行完++(++p1)以后p1.m_num= " << p1 << endl;
}
void test03()
{
myint p2;
cout << "p2++= " << p2++ << endl;
cout << "运行完p2++以后p2.m_num= " << p2 << endl;
}
int main()
{
test01();
test03();
}
上面这一段代码,有几个点是需要注意一下的。第一个就是声明左移运算符重载的时候,参数设置问题:
Q1:为什么传参是,myint要传对象,而不是引用,引用的方式test03()就会报错?
这个问题,我思考了一段时间,也在csdn找到了一些答案,说法看似有两种,其实感觉都是讲述同一个东西:就是匿名对象不能被非常引用初始化。
我现在说一下,我自己的见解:运行完后置运算重载函数后,返回的是一个临时的匿名对象。因为临时对象\变量不能被初始化引用,还有种解释:临时、匿名对象具有常性不能作为左值。这里又会牵扯出一个问题就是:
非常量引用为什么不能接收函数的返回值?
要解决这个问题就是得去了解左值与右值(c++真是,越学越废了)。左值在c++11中,可以取地址的、有名字的。右值不能取地址的、没有名字的就是右值。比如int a=b+c;a是一个变量,我们知道a是有自己的地址以及变量名的,显然a就是左值。表达式b+c,以及在函数的返回值,在没有给他赋值一个某一个变量时,我们都不能通过变量名找到他,也叫临时变量,执行完当前行以后,就会销毁。这两个就是典型的右值。
介绍完左值右值,现在说一下非常量左值引用以及常量左值引用。我们常见的int &a=b;就是非常量左值的语法;左值引用就是给该对象取一个别名,没有新开辟了一个内存空间。一般当我们想传入一个参数给函数时,如果使用值传递或者非常量引用传递,是告诉编译器想修改实参的值。我们使用常量引用时,我们想告诉编译器,我们不想改变实参的值。针对临时变量,编译器没有告诉我们,生成临时变量的规则,我们不知道其变量名,自然也无法正常访问到他们,所以当我们使用非常量引用传递这个临时变量时,我们在这个函数里面对这个临时变量进行修改以后,我们也无法得到这个修改的值,因为我们压根找不到他!所以我们不能使用非常量左值引用去引用一个临时变量。
现在我们可以解决左移运算符重载函数形式参数类型的问题了。有两种方法可以解决:
1、使用值传递,直接把这个临时对象copy给形参。
2、使用常量左值引用(常量左值引用是开辟一块空间来储存这个临时变量,所以以后就找的到这个临时变量了,只是用这种方法,函数无法改变这个临时变量的值,这个是和右值引用的区别)
对象析构后,对象里面的成员属性值调用问题
针对这个问题,我写了一段代码:
#include<iostream>
#include<string>
using namespace std;
class myint
{
public:
int m_num;
string name;
myint(string s)
{
this->name = s;
m_num = 0;
cout << this->name <<"的构造函数" << endl;
}
~myint()
{
cout << this->name<<"的运行析构函数" << endl;
}
};
myint& test01()
{
myint p1("p1");
p1.m_num = 1;
cout << "ssss" << endl;
return p1;
}//在执行完test01以后,p1明明已经被析构了,而且返回的也是这个对象的引用。也就是说这个引用指向的对象已经被清除了。为什么test还能打印p.num?
void test(myint& p)
{
cout << "wwww" << endl;
//p.m_num = 6;
cout << p.m_num << endl;
}
int main()
{
myint o("o");
o= test01();
test(o);
}
上述代码中,我在test01中,我对 对象屏p1进行了实例化,并且对成员属性进行了修改。我在写完这个代码后,有一个疑问:在运行完tesr01以后,对象不是被析构了吗,按道理p1.m_num=1;这个值将不复存在的。为什么我在后面对象o调用m_num还能输出1呢?
有一些回答是:将test01()的返回值拷贝给对象o的时候,p1还没有被析构。在执行完这一行才会被析构。这一点我是不赞同的(当然,我没有证据)
我得想法是:在o在拷贝p1的成员属性值时,p1已经被析构了,只不过test01()返回了一个匿名对象(这个对象还存着关于p1的信息)又因为临时变量要执行完当前行才会被清除,所以在这个匿名对象被清除时,已经完成o的初始化了。