iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >详解C++ 运算符重载中返回值的坑
  • 781
分享到

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

2024-04-02 19:04:59 781人浏览 泡泡鱼
摘要

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

相信不少朋友在学习运算符重载的时候,都会被参数与返回值应该是左值引用,还是右值引用,还是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++ 运算符重载返回值内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 详解C++ 运算符重载中返回值的坑

本文链接: https://www.lsjlt.com/news/122896.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

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

下载Word文档
猜你喜欢
  • c语言怎么计算字符串长度
    在C语言中,可以使用内置的strlen()函数来计算字符串的长度。该函数位于string.h头文件中。以下是一个示例代码: #inc...
    99+
    2024-05-15
    c语言
  • c语言怎么判断三个数的大小
    可以通过比较三个数之间的大小关系来判断它们的大小。以下是一个示例代码: #include <stdio.h> int ...
    99+
    2024-05-15
    c语言
  • c++排序函数sort怎么使用
    在C++中,使用sort函数对容器中的元素进行排序,可以按照默认的升序排列,也可以自定义排序规则。 下面是一个简单的例子,对一个ve...
    99+
    2024-05-15
    c++
  • c++中string的compare函数怎么使用
    在C++中,可以使用string的compare函数来比较两个string对象的大小。该函数的原型如下: int compare(c...
    99+
    2024-05-15
    c++
  • c++中string的compare函数有什么用
    c++中string的compare函数用于比较两个字符串的大小关系。该函数返回一个整数值,表示两个字符串的比较结果。 具体来说,如...
    99+
    2024-05-15
    c++
  • c++中print函数的功能有哪些
    在C++中,通常使用std::cout来进行输出操作,而不是使用print函数。std::cout是C++标准库中的一个输出流对象,...
    99+
    2024-05-15
    c++
  • c语言数据结构怎么使用
    在C语言中,数据结构可以通过定义结构体来实现。以下是一些常见的数据结构的使用方法示例: 链表: #include <std...
    99+
    2024-05-15
    c语言
  • c++中print函数的用法是什么
    在C++中没有内置的print函数,但可以使用cout来输出内容到控制台。cout是C++标准库中的一个输出流对象,可以使用<...
    99+
    2024-05-15
    c++
  • c++中concept的用法是什么
    在C++20中,Concept是一种新的语言特性,用于定义类型要求和约束。Concept可以被用来约束函数模板、类模板和普通函数的参...
    99+
    2024-05-15
    c++
  • c++中concept的作用是什么
    在C++中,concept的作用是定义一种通用的约束,用于限制模板参数的类型范围。通过使用concept,可以在编译时对模板参数进行...
    99+
    2024-05-15
    c++
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作