C++ 運算子多載中回傳值的坑
- E0334 "Myclass" 沒有適當的復制建構式
- E0349 沒有與這些運算元匹配的 "<<" 運算子
- C3861 “function”: 找不到識別符號
- Summary
相信不少朋友在學習運算子多載的時候,都會被引數與回傳值應該是左值參考,還是右值參考,還是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://www.uj5u.com/qita/272189.html
標籤:其他
上一篇:編程基本功:學會抄,自然就會創新
