程序员

C++ 运算符重载中返回值的坑

作者:admin 2021-04-06 我要评论

C 运算符重载中返回值的坑 E0334 Myclass 没有适当的复制构造函数 E0349 没有与这些操作数匹配的 运算符 C3861 “function”: 找不到标识符 Summary 相信不少朋...

在说正事之前,我要推荐一个福利:你还在原价购买阿里云、腾讯云、华为云服务器吗?那太亏啦!来这里,新购、升级、续费都打折,能够为您省60%的钱呢!2核4G企业级云服务器低至69元/年,点击进去看看吧>>>)

相信不少朋友在学习运算符重载的时候,都会被参数返回值应该是左值引用,还是右值引用,还是const常量所困扰。当然我无法一一枚举,这次先讲一下返回值的坑 (没错就是我亲手写的bug)

E0334 “Myclass” 没有适当的复制构造函数

其实这个问题的根源是,没有定义常量参数类型的拷贝构造函数所致
先来看看代码

//头文件head.h
class Myclass
{
private:
	int a;
public:
	Myclass(int b=0):a(b) {}		//构造函数
	Myclass(Myclass& c);			//复制构造函数
	~Myclass(){}					//析构函数
	Myclass operator+(Myclass& d);	//重载+运算符
	friend ostream& operator<<(ostream& os ,const Myclass& d);
									//重载<<运算符
};
//以下是定义
Myclass::Myclass(Myclass& c)
{
	a = c.a;
}
Myclass Myclass::operator+(Myclass& d)
{
	return Myclass(d.a+a);			//!!此处报错
}
ostream& operator<<(ostream& os,const Myclass& d)
{
	os << d.a << std::endl;
	return os;
}
//main.cpp
#include"head.h"
int main()
{
	Myclass a1(5);
	Myclass a2(12);
	Myclass sum = a1 + a2;		//!!此处报错
	std::cout << sum;
}

代码在VS中,又出现了令人讨厌的小红线,没有适当的复制构造函数,这就有疑问了, 不是明明有个构造函数Myclass(int b=0):a(b) {}吗,参数是int很合适啊? 于是,我们定义一个临时变量temp,再将它返回,此时会隐式调用拷贝构造函数而后返回一个副本后原来的temp就die了,因此返回值不可以是引用。下面是代码

Myclass Myclass::operator+(Myclass& d)
{
	Myclass temp(d.a + a);
	return temp;
}

此时第一处报错消失了,但是第二处报错依然存在,而且仍为 “没有适当的复制构造函数”这就说明了,我的入手方向应该是拷贝构造函数
经过博主的调试,得知是因为函数的返回值是一个纯右值,为了验证这个想法,使用了右值引用,来接收这个纯右值(当然,右值引用更多的是用在移动构造函数上,将 将亡值“偷”出来

#include"head.h"
int main()
{
	Myclass a1(5);
	Myclass a2(12);
	Myclass&& sum = a1 + a2;
}

果然,它不报错了
但是考虑到实用性,总不能让用户今后做个加法都要用右值引用接收吧,因此,我们要从源头解决,即重载拷贝构造函数。
值得思考的是,右值不就是被赋值的那个吗,为什么用Myclass&& sum = a1 + a2;无法赋值呢?众所周知,Myclass&& sum = a1 + a2;调用的是拷贝构造函数,类不同于基本数据类型,它要通过程序员来设置一系列的功能,我们没有设置接受,Myclass类型的右值的功能,只定义了接受int类型的右值的功能,这自然是不行的了。
因此,重载拷贝构造函数

Myclass::Myclass(const Myclass& c)
{
	a = c.a;
}

此时就能运行了

E0349 没有与这些操作数匹配的 “<<” 运算符

关于流运算符为什么要写成$ostream& operator<<(ostream& os,const Myclass& d); 而非ostream& operator<<(ostream& os,Myclass& d);这个问题,网上绝大部分的回答都是输出没必要修改值。那么我们先定义后者

#head.h
#pragma once
#include<iostream>
using std::ostream;
class Myclass
{
private:
	int a;
public:
	Myclass(int b=0):a(b) {}
	Myclass(Myclass& c);
	Myclass(const Myclass& c);
	~Myclass(){}
	Myclass operator+(Myclass& d);
	friend ostream& operator<<(ostream& os ,Myclass& d);
};
Myclass::Myclass(const Myclass& c)
{
	a = c.a;
}
Myclass::Myclass(Myclass& c)
{
	a = c.a;
}
Myclass Myclass::operator+(Myclass& d)
{
	Myclass temp(d.a + a);
	return temp;
}
ostream& operator<<(ostream& os,Myclass& d)
{
	os << d.a << std::endl;
	return os;
}
#main.cpp
#include"head.h"
int main()
{
	Myclass a1(5);
	Myclass a2(12);
	Myclass&& sum = a1 + a2;		
	std::cout << a1 + a2;				//此处有讨厌小红线
}

不难发现,讨厌的小红线又出来了。
我们可以想象一下这个过程,a1.operator+(a2),返回了个临时变量,暂且假设它叫newguy,那么newguy为一个右值,又调用了函数os.<<(&d),传参为&d=newguy,现在问题来了,左值引用怎么能够接受一个纯右值呢? 而我们定义的重载的流运算符接受的参数类型为左值,我们并没有给出从左值到右值强制类型转换的函数,但是在上一部分,我们给出了从右值到左值的拷贝构造函数,因此,将流运算符声明为前者更好。

C3861 “function”: 找不到标识符

这个问题应该是非常常见的,不习惯将函数(或是类)先声明后定义而又喜欢让函数(或是类)相互调用,但是在类模板它比以上两种更为隐蔽。

#include<iostream>
class A
{
	friend void show();			//“声明”函数
	friend void show1();		//“声明”函数
}; 
void show()						//定义
{
	show1();
}
void show1(){}					//定义
int main()
{
	A a;
	show();						//调用
	show1();					//调用
}

以上流程看似声明->定义->调用非常完美,实则还是会报错的,不过跟以上两种不一样的是,它是在linking的时候出错,这是为什么呢?
原来友元函数并不属于这个类的一部分,在类内定义仅仅是为了告诉编译器“这个函数是这个类的友元函数”,并没有对这个函数本身进行声明,因此,正确的做法应该是这样的:

#include<iostream>
void show();
void show1();
class A
{
	friend void show();
	friend void show1();
}; 
void show()
{
	show1();
}
void show1(){}
int main()
{
	A a;
	show();
	show1();
}

Summary

本文主要讲了三点。
首先,要注意将拷贝构造函数重载
其次,要将流运算符<<的参数类型确定为(ostream&,const myclass&),当然,istream则万万不可const,ostream是没有拷贝构造函数的,因此引用也是必须的。
最后,类内友元函数的声明,并不等同于函数本身的声明。

(由于本人水平有限,疏忽错误在所难免,还请各位指正)

;原文链接:https://blog.csdn.net/txf555/article/details/115419193

版权声明:本文转载自网络,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。本站转载出于传播更多优秀技术知识之目的,如有侵权请联系QQ/微信:153890879删除

相关文章
  • Vue SPA 首屏优化方案

    Vue SPA 首屏优化方案

  • vue 动态添加的路由页面刷新时失效的原

    vue 动态添加的路由页面刷新时失效的原

  • vue项目配置 webpack-obfuscator 进行

    vue项目配置 webpack-obfuscator 进行

  • JavaScript 中的执行上下文和执行栈实

    JavaScript 中的执行上下文和执行栈实

腾讯云代理商
海外云服务器