关于C++前置++及后置++重载的问题

先了解一些名词的实质意义:

临时变量:True temporary objects in C++ are invisible — they don't appear in your source code. They arise whenever a non-heap object is created but not named.--《More Effective C++》

真正的临时变量是不可见的(平时所创建的一般是局部变量),每当创建非堆对象但未命名时,它们就会出现[1]

void fun(const string& str)
{}
int main(int argc, char* argv[])
{
	char a[] = "Local Variable";//临时变量并不可见,这里时局部变量
	fun(a);  // 有类型转换
	return 0;
}

这里fun函数要const string&类型参数,实参确是char*,所以编译器会进行类型转换,创建了一个string类型的临时变量(对象),并用a进行初始化,之后将此变量传给函数fun,

如果是这样:

void fun(string& str)
{}
int main(int argc, char* argv[])
{
	char a[] = "Local Variable";//临时变量并不可见,这里时局部变量
	fun(a);  // 有类型转换,err
	return 0;
}

此时,若创建一个临时变量,如果在函数内部对这个临时变量进行修改就会产生误会(我明明传的是这个,出来居然不是),所以这里就发挥了const的作用,既然不想更改就明确说明用const

可能有人会说那为什么不直接用值传递?--看下面情况一

产生临时变量的三种情况:1.By Value方式传值,2.参数const类型,3.类型转换[1]

一:以By Value的方式传值。
        我们都知道,引用类型和指针类型传递的都是地址,可以直接对地址中存放的数据进行操作,而以传值的方式传递参数,就会在heap中重新分配一个临时区域,将实参中的数据拷贝到临时区域中,而你对这份数据进行的任何的操作都不会影响实参的内容,因为实参跟形参只是内容相同,分别在两块不同的内存中。而引用和指针操作的是同一块内存,所以形参修改后,实参也修改了。
二:参数为const的类型。
        因为常量是不能修改,在只需要实参中的数据,而不需对实参进行修改时,或是禁止对实参进行修改时,把形参定义为const类型,系统会产生一个临时变量,就能起到保护数据的作用,如在函数strlen中,修改参数的值行吗?本来只是想得到实参的长度,结果在函数中被修改了,那得到得实参长度还是真实的吗。
        如果你程序中的数据到处都可以被修改,那是多么的可怕(所以我们讨厌全局变量),所以const还是有它存在的价值。
三:类型转换的时候会产生临时变量。
        真是糟糕啊,在用类型转换带来便利的同时,产生临时变量就是我们承担的损失。
        如将一个short类型转换成int类型,他们占用的内存不一样,如果不产生临时变量,那不就short类型和int类型占用的字节数不就一样了吗,sizeof不就坑爹了吗。

     C++语言禁止为非常量引用产生临时对象。同时证明引用类型传参不会产生临时变量,如char[]转换成string会报错,他们都是引用类型。

        临时变量不能作为非const引用参数,不是因为他是常量,而是因为c++编译器的一个关于语义的限制。如果一个参数是以非const引用传入,c++编译器就有理由认为程序员会在函数中修改这个值,并且这个被修改的引用在函数返回后要发挥作用。但如果你把一个临时变量当作非const引用参数传进来,由于临时变量的特殊性,程序员并不能操作临时变量,而且临时变量随时可能被释放掉,所以,一般说来,修改一个临时变量是毫无意义的,据此,c++编译器加入了临时变量不能作为非const引用的这个语义限制,意在限制这个非常规用法的潜在错误。

临时变量只能通过Const引用来指向,因为临时变量不可修改[2]

接着[3]

#include<iostream>
using namespace std;

class Complex{
public:
	Complex() :_real(0), _imaginary(0) {}
	Complex(double real, double imaginary) :_real(real), _imaginary(imaginary) {}
	Complex(Complex& cp) :_real(cp._real), _imaginary(cp._imaginary) {}
	friend Complex getConjugate(Complex cp);
private:
	double _real;             //实部
	double _imaginary;        //虚部
};
//求共轭复数
//为Complex的友元
Complex getConjugate(Complex cp){
	cp._imaginary = -cp._imaginary;
	return cp;
}
int main()
{
	Complex a(1.0, -5.0);
	Complex b = getConjugate(a);  // Error : no match
	return 0;
}
error C2440: 'initializing': cannot convert from 'Complex' to 'Complex'
Cannot copy construct class 'Complex' due to ambiguous copy constructors or no available copy constructor

Complex b = getConjugate(a);调用了拷贝构造,但是拷贝构造中形参是Complex& cp根据上面的解释,这样是不允许的,所以需要const Complex& cp

还有上面等式调用了两次拷贝构造,第一次是传参创建了临时对象需要用a这个旧对象初始化新对象(即那个临时对象),第二次则是给b赋值(这个很好理解)

终于到了讨论标题内容的时候了

先添加两端代码:是前置++后置++的两种实现方式

1.友元函数实现

#include <iostream>
using namespace std;
class Complex
{
	friend Complex &operator++(Complex &c);  //前置++
	friend Complex operator++(Complex &c, int);//后置++, 多了一个占位符

public:
	Complex(int x = 0, int y = 0) :a(x), b(y) {}

	void display() const{
		cout << a << " + " << b << "i" << endl;
	}

private:
	int a, b;
};
//前置++,先加后用
Complex &operator++(Complex &c){
	c.a++;
	c.b++;

	return c;
}
//后置++, 先用后加
Complex operator++(Complex &c, int){
	Complex tmp = c;
	c.a++;
	c.b++;
	return tmp;
}
int main()
{
	Complex c(1, 2);
	//前置++, 先加后用
	Complex tmp = ++c; //函数原型: operator++(Complex &c);
	tmp.display();
	c.display();
	//后置++, 先用后加,多了一个占位符参数int,表示后置操作
	Complex tmp2 = c++; //函数原型: operator++(Complex &c, int);
	tmp2.display();
	c.display();

	return 0;
}

2.成员函数实现

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Plusplus
{
public:
	friend ostream& operator<<(ostream&os, const Plusplus&myint);
	Plusplus(int val){
		mNum = val;
	}
	/**拷贝构造,用户定义了普通构造,系统则不提供无参构造,但提供拷贝构造
	Plusplus(const Plusplus&plusplus)
	{
		mNum = plusplus.mNum;
	}
	*/
	//前置++运算符重载
	Plusplus& operator++(){
		//先++,再返回对象,返回值类型是引用类型
		++mNum;
		return *this;
	}
	//后置++运算符重载
	Plusplus operator++(int){
		//先返回一个临时对象,再++,返回值类型是值类型
		Plusplus tmp(*this);
		++mNum;
		return tmp;
	}
private:
	int mNum;
};

ostream& operator<<(ostream&os,const Plusplus&myint){
	os << myint.mNum;
	return os;
}

void test(){
	/**
	有参构造调用的三种方式
	* Plusplus myint = 10;
	* Plusplus myint = Plusplus(10);
	*/
	Plusplus myint(10);
	cout << ++myint << endl;
	cout << myint++ << endl;
	cout << myint << endl;
	
}

int main()
{
	test();
	//VS中能引用匿名对象,err,需要右值引用
	//Plusplus &my = Plusplus(10);
	system("pause");
	return EXIT_SUCCESS;
}

上面两端代码中有两点不明白:[4]

1.前置++跟后置++实现用的是同一个函数名通过形参不同(形参中多了一个int作为占位符)重载实现的,这里面估计编译器做了优化当调用param++的时候转化成了operator++(param,0),当++param则会转化成operator++(c)再调用对应的函数。

2.为什么友元函数实现方式中函数形参比成员函数多一个参数:因为友元函数是全局函数,其中并没有指向类对象的this指针,所以要另外引入一个引用该类对象的形参传递数据

 

[1] C++中的临时变量 https://blog.csdn.net/wxn704414736/article/details/79972570

[2] C++临时变量的生命周期 https://www.cnblogs.com/catch/p/3251937.html

[3] C++11移动改语义探讨--从临时对象到右值引用 https://blog.csdn.net/y1196645376/article/details/54911331

[4] C++成员运算符重载和友元运算符重载的比较(以++,--运算符为例)https://blog.csdn.net/ayangya/article/details/78901294

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值