我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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

相信不少朋友在学习运算符重载的时候,都会被参数与返回值应该是左值引用,还是右值引用,还是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();
}

总结

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

到此这篇关于详解C++ 运算符重载中返回值的坑的文章就介绍到这了,更多相关C++ 运算符重载返回值内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C++运算符重载和返回值优化的方法

本篇内容介绍了“C++运算符重载和返回值优化的方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!问题背景  编译环境还是
2023-06-30

C++中运算符重载问题详解

C++中运算符重载问题详解运算符重载是C++中的一个重要特性,它允许我们为自定义的类类型重载运算符,使得这些类对象之间的运算能够像内置类型一样进行。在本文中,我们将详细讨论C++中运算符重载的概念、使用方法以及常见的问题。一、什么是运算符重
2023-10-22

C++中的运算符重载问题详解

C++中的运算符重载问题详解运算符重载是C++中一个强大而有用的特性,通过重载运算符,可以使得某个类的对象可以像基本类型数据一样使用各种运算符,方便地进行各种操作。本文将详细解释运算符重载的概念,并提供具体的代码示例。在C++中,运算符重载
2023-10-22

C++重载的奥义之运算符重载详解

函数的重载是指利用相同的函数名设计一系列功能相近,但是功能细节不一样的函数接口;因此运算符重载也是指对于同一个运算符来说,它可以用于实现不同的功能。下面就一起来理解下运算符重载的应用吧
2023-05-16

C++中怎么重载赋值运算符

这篇文章给大家介绍C++中怎么重载赋值运算符,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。重载赋值运算符在类中重载赋值运算符的格式如下:void operator = (const Date&)后面我们回加以改进。目前
2023-06-17

详解C++编程中一元运算符的重载

可重载的一元运算符如下:!(逻辑“非”)&(取址)~(二进制反码)*(取消指针引用)+(一元加)-(一元求反)++(递增)--(递减)转换运算符后缀递增和递减运算符(++ 和 ??)在递增和递减中单独处理,下面会讲到。 以下规则适用于所有其
2022-06-04

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录