我在下面的代码中有冲突。
#include <iostream>
using std::cout;
using std::endl;
class TestApp {
public:
TestApp(int _x = 9) {
cout << "default constructor\n";
}
TestApp(const TestApp &app) {
cout << "Copy constructor\n";
}
~TestApp() {
cout << "destructor\n";
}
void setX(int _x) {
}
const TestApp &operator=(TestApp &obj) {
cout << "= operator\n";
return *this;
}
void setX(int _x) {
cout << "Inside setX\n";
}
};
int main() {
TestApp app1;
TestApp app2;
TestApp app3;
(app1 = app2) = app3; // 1
app1 = app2 = app3; // 2
app1.setX(3)
return 0;
}
我收到此错误:第1行main.cpp:38: error: passing ‘const TestApp’ as ‘this’ argument of ‘const TestApp& TestApp::operator=(TestApp&)’ discards qualifiers
但是,我可以使用app1.setX(3);
main.cpp:38: error: no match for ‘operator=’ in ‘app1 = app2.TestApp::operator=(((TestApp&)(& app3)))’
main.cpp:28: note: candidates are: const TestApp& TestApp::operator=(TestApp&)
要使其正常工作,我应该operator=
像:
TestApp &operator=(const TestApp &obj) {
cout << "= operator\n";
return *this;
} // works for 1
TestApp &operator=(TestApp &obj) {
cout << "= operator\n";
return *this;
} // works for 2
为什么我删除const
关键字有效?在第1行app1
对象不是常数之后。
答案 0 :(得分:2)
提供赋值运算符的正确方法是将其声明如下:
TestApp &operator=(const TestApp &obj) {
cout << "= operator\n";
return *this;
}
请注意,右侧操作数前面只有一个const
,操作符本身及其返回值未声明为const
。
声明运算符const
是错误的,因为赋值运算符意味着来修改this
对象。
它不必要地限制了使用运算符返回const
引用,因为调用者已经为您提供了非const
引用。因此,调用者已经对该对象进行了非const
访问,因此返回const
引用会不必要地阻止他在非const
上下文中重用返回值
执行双重赋值app1 = app2 = app3;
时会发生这种情况:它被评估为app1 = (app2 = app3);
,因此一个赋值的返回值作为右侧参数传递给下一个赋值。第一个赋值返回的非const
引用可以隐式转换为const
引用,因此可以正常工作。
如果您的编译器使用上面给出的声明抱怨您的第2行,那么您的编译器应该受到指责。我用gcc 4.7.2检查了下面的代码,它工作正常:
class Foo {
public:
Foo() {};
Foo(const Foo& other) {}
Foo& operator=(const Foo& other) { return *this; }
};
int main() {
Foo foo1, foo2, foo3;
(foo1 = foo2) = foo3;
foo1 = foo2 = foo3;
}
答案 1 :(得分:2)
您可能无法分配常量对象。例如,考虑这个简单的代码
const int x = 10;
x = 20;
编译器将为第二个语句发出错误,因为x是一个常量对象,可能无法分配。
同样适用于陈述
(app1 = app2) = app3;
此处表达式(app1 = app2)
返回可能未分配的常量引用,
常量引用并不意味着它引用的对象本身是不变的。请考虑以下示例
int x = 10;
const int &rx = x;
x = 20;
rx = 30;
虽然rx被定义为常量引用,但您可以更改对象x本身。您可能不会使用该引用来分配对象x,因此编译器将为最后一个语句发出错误。
我们经常在函数的参数声明中使用常量引用,以防止更改它们在函数内部引用的对象。例如
void f( const int &x )
{
x = 20; // compilation error
}
int x = 10;
f( x );
因此,定义对非常量对象的常量引用不会使对象本身保持不变。它只能阻止使用此引用更改对象。
您只需要定义一个复制赋值运算符
TestApp &operator=(const TestApp &obj) {
cout << "= operator\n";
return *this;
} // works for 1
无需将复制赋值运算符定义为
TestApp &operator=(TestApp &obj) {
cout << "= operator\n";
return *this;
} // works for 2
如果你不打算改变正确的操作数。因此,最好将其定义为常量引用const TestApp &obj
当然,您可能将这两个运算符放在一起,但没有任何意义可以使用第二个运算符。
另一方面,您可能没有第二个操作员。在这种情况下,您将无法使用将它们分配给其他对象的常量对象。