使用dev c++进行一波教科书代码的练习时出现了一点问题,于是来记录一波。
报错信息如下:
[Error] cannot bind non-const lvalue reference of type 'std::String&' to an rvalue of type 'std::String'
解决方案:检查一下是不是需要const引用
以下是问题记录,大家可以酌情浏览(因为我的废话太多)
一、问题描述
(一)原始问题
在练习字符串运算符重载,按照书上的原代码进行了一波敲:
(大家就关注一下运算符=和+的重载就可以了,运算符>的重载我并没有用到)
/*P225*/
#include <iostream>
#include <cstring>
using namespace std;
namespace std
{
class String //定义字符串类
{
protected:
int Length;
char *Sp;
public:
String() //定义缺省构造函数
{
Sp = 0;
Length = 0;
}
String (const String &); //定义复制构造函数
String (const char *s) //定义初始化构造函数
{
Length = strlen(s);
Sp = new char[Length+1];
strcpy(Sp, s);
}
~String()
{
if(Sp) delete[]Sp;
}
void Show()
{
cout<<Sp<<endl;
}
void operator=(String &); // 定义字符串赋值成员函数
friend String operator+(const String &, const String &); //定义字符串拼接友元函数
int operator>(const String &); //定义字符串比较成员函数
};
String::String(const String &s)
{
Length = s.Length;
if(s.Sp)
{
Sp = new char [Length+1];
strcpy(Sp, s.Sp);
}
else Sp = 0;
}
void String::operator=(String &s)
{
if(Sp) delete[]Sp;
Length = s.Length;
if(s.Sp)
{
Sp = new char [Length+1];
strcpy(Sp, s.Sp);
}
else Sp = 0;
}
String operator+(const String &s1, const String &s2)
{
String t;
t.Length = s1.Length + s2.Length;
t.Sp = new char[t.Length+1];
strcpy(t.Sp, s1.Sp);
strcat(t.Sp, s2.Sp);
return t;
}
int String::operator>(const String &s)
{
int ret;
if(strcmp(Sp,s.Sp)>0) ret = 1;
else ret = 0;
return ret;
}
}
int main()
{
String s1("SW"), s2("HW"), s3("DS");
String s4(s1), s5, s6, s7;
s5 = s2;
s6 = s4+s3;
s7 = s5+s3;
s6.Show();
s7.Show();
return 0;
}
然后报错了:
(二)、为什么在创建对象的同时进行赋值就没有问题?
我第一次学这个也不知道到底是+的问题还是=的问题,所以我在主函数这里进行了一波微小的改变:
由此看出:如果我先建立s6,s7的对象,再对s6, s7进行拼接操作,就会报错;而如果我在建立对象的同时进行赋值,显然就没有出错。
二、问题解决
我以为是+的问题,但我后来突然发现,字符串赋值成员函数operator=的参数不是const类型:
修改成operator=(const &)后进行一波尝试:
/*P225*/
#include <iostream>
#include <cstring>
using namespace std;
namespace std
{
class String //定义字符串类
{
protected:
int Length;
char *Sp;
public:
String() //定义缺省构造函数
{
Sp = 0;
Length = 0;
}
String (const String &); //定义复制构造函数
String (const char *s) //定义初始化构造函数
{
Length = strlen(s);
Sp = new char[Length+1];
strcpy(Sp, s);
}
~String()
{
if(Sp) delete[]Sp;
}
void Show()
{
cout<<Sp<<endl;
}
void operator=(const String &); // 定义字符串赋值成员函数
friend String operator+(const String &, const String &); //定义字符串拼接友元函数
int operator>(const String &); //定义字符串比较成员函数
};
String::String(const String &s)
{
Length = s.Length;
if(s.Sp)
{
Sp = new char [Length+1];
strcpy(Sp, s.Sp);
}
else Sp = 0;
}
void String::operator=(const String &s)
{
if(Sp) delete[]Sp;
Length = s.Length;
if(s.Sp)
{
Sp = new char [Length+1];
strcpy(Sp, s.Sp);
}
else Sp = 0;
}
String operator+(const String &s1, const String &s2)
{
String t;
t.Length = s1.Length + s2.Length;
t.Sp = new char[t.Length+1];
strcpy(t.Sp, s1.Sp);
strcat(t.Sp, s2.Sp);
return t;
}
int String::operator>(const String &s)
{
int ret;
if(strcmp(Sp,s.Sp)>0) ret = 1;
else ret = 0;
return ret;
}
}
int main()
{
String s1("SW"), s2("HW"), s3("DS");
String s4(s1), s5, s6, s7;
s5 = s2;
s6 = s4+s3;
s7 = s5+s3;
s6.Show();
s7.Show();
return 0;
}
就没有问题了:
三、一些写给自己的补充
那么,让我们想想原理吧!
(一)、为什么必须要const引用?
好吧,为了更好地理解,我至少要搞清楚以下这件事情:
我把临时变量和局部变量的概念搞混淆了。
天真的我还以为把临时变量改成全局变量是不是就有编译通过的可能,太菜了
菜狗就是这样的,基础不牢T.T
我是看到这几篇文章才意识到原来我的基础问题这么大:
在C/C++中,真正意义上的临时变量是看不见的,就是说它们不会出现在你的代码中,参考一下这句话(来自《More Effective 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.
至此其实我仍然不能理解……但总之我现在记住了:临时变量不能作为非const引用参数,所以那里要需要const引用。
(二)、为什么临时变量作为const引用参数时,创建对象同时进行赋值就没有报错?
问了老师,说是“初始化”与“赋值”的问题。
比如说数组可以初始化,但不可以定义后再整体赋值。如以下代码编译可通过:
#include <iostream>
using namespace std;
int main()
{
int b[5] = {1, 2, 3, 4, 5};
int a[2];
//a[5] = {1, 2, 3, 4, 5}; //wrong
a[0] = 1;
a[1] = 2;
return 0;
}
赋值和初始化是肯定不一样的。赋值是指擦除当前值,用新值替代,而初始化是指创建变量的同时赋予初始值。赋值不一定初始化,初始化一定有赋值。
在这篇文章里,作者提到:
2.拷贝初始化:使用等号(=)初始化一个变量,实际上执行的是拷贝初始化,编译器把等号右侧的初始值拷贝到新创建的对象中去,拷贝初始化通常使用拷贝构造函数来完成。拷贝初始化不仅在我们使用=定义变量时会发生,在下列情况也会发生(1)将一个对象作为实参传递给一个非引用类型的形参。(2)从一个返回类型为非引用类型的函数返回一个对象。
我认为【s6 = s4+s3;】应该属于拷贝初始化
但是再往深入我就看不懂了,我太弱了,c++还有很多东西等着我去学习