const 方法使用引用修改对象
Posted
技术标签:
【中文标题】const 方法使用引用修改对象【英文标题】:const method modifies object using reference 【发布时间】:2018-06-13 11:31:52 【问题描述】:以下代码调用const
方法传递对成员的引用,然后对其进行修改。
#include <iostream>
struct A
int i;
A(int _x) : i(_x)
void calc(int& j, const int value) const j = value;
void set1() calc(i, 1);
;
int main()
A a(3);
std::cout << a.i << std::endl;
a.set1();
std::cout << a.i << std::endl;
return 0;
代码使用 gcc 6.4.0 和 clang 5.0.2 编译,没有警告。
代码合法吗?
当从 non-const
方法调用时,const
方法 calc
能够修改对象。
【问题讨论】:
除非您的对象不是const
,否则const
说明符没有任何区别。如果a
是const
,您将无法调用A::set1
。
在void calc
尝试i = value;
。然后你会突然得到error: assignment of member ‘A::i’ in read-only object
....或者尝试设置const int& j
。然后你会得到error: assignment of read-only reference ‘j’
这是一个很好的例子,为什么您不应该将原始字段公开为可公开访问的成员。
@YurySchkatula 将其设为私有不会改变行为
【参考方案1】:
成员函数上的const
限定符适用于*this
实例。
在calc()
中,this
是指向 const A
的指针,但参数 j
是由非常量引用获取的,因此这是完全标准的行为。
现在,如果您尝试在 calc
中分配给 this->i
,代码将无法编译。
void A::calc(const int value) const
i = value; // Compilation error here: i is a data member of a const instance
同理,如果set1
是一个const 成员函数,那么代码将无法编译(因为它会尝试将this->i
绑定到非const 引用获取的参数)
【讨论】:
【参考方案2】:当然。标记方法const
只会使*this
const
,即该函数承诺不会修改对象通过写入this
。
仍然可以通过其他方式修改对象(假设它们也没有标记为const
,例如您的示例中的int& j
)。
【讨论】:
其他方式比如投射this
@GauravSehgal 不,我的意思是使用引用对象(部分)的其他指针或引用。抛弃 constness 是很顽皮的(如果对象实际上是 const
,很快就会导致 UB)。【参考方案3】:
请记住,拥有像const Thing*
这样的“const 指针”或像const Thing&
这样的“const 引用”并不意味着当您拥有指针/引用时,const 限定对象不能更改。这仅意味着您不能使用该特定指针/引用作为更改它的一种方式。但可能还有其他名称、指针或引用确实允许对其进行更改。
几个例子:
void f1(const int& arg1, int& arg2)
std::cout << "arg1 before: " << arg1 << "\n";
arg2 = 4;
std::cout << "arg1 after: " << arg1 << "\n"; // same thing?
f1
看起来好像必须始终在“之前”和“之后”行中打印相同的值。但如果有人将相同的 int
对象传递给两个参数,则不会:
void call_f1()
int n = 7;
f1(n, n); // Prints before 7, after 4!
或者,如果函数调用发生在 const 引用的两次使用之间,那同样可以以某种方式更改变量:
void something_else();
void f2(const int& arg)
std::cout << "arg before: " << arg << "\n";
something_else();
std::cout << "arg after: " << arg << "\n";
int n = 2;
void something_else() n = 8;
void call_f2()
f2(n); // Prints before 2, after 8!
所以在您的void A::calc(int& j, const int value) const
函数中,this
指针确实是const A* const
,这意味着您不能使用this
指针更改A
对象。但是仍然可以通过其他方法来更改它,例如在这里您有一个 int& j
对非常量对象的引用。如果碰巧j
引用了*this
的子对象,那么修改j
是修改*this
的子对象的有效方法。这类似于我上面的f1
示例,其中arg1
不能用于更改引用的int
,但arg2
可以,如果它们引用相同的int
,则表示arg1
变了。
当变量定义首先使用const
限定符时,情况略有不同。如果我们写
const A a(3);
那么我们确实得到了保证(除了在构造函数和析构函数期间),对象不能以任何方式改变。该语言通常会阻止您意外尝试,例如使用a.set1()
,但即使您尝试const_cast
技巧,任何实际更改都将是未定义的行为。
【讨论】:
【参考方案4】:您的代码没有任何问题。声明一个方法const
仅仅意味着this
是常量。但是,您的方法不会(直接)修改this
或this
的任何成员。考虑这个人为的,虽然是正确的例子:
struct foo
int value;
void modify_const(foo& f) const f.value = 5;
;
int main()
foo f;
f.value = 3;
f.modify_const(f);
该方法没有修改this
,并且参数被声明为非常量,因此在const f
上调用f.modify_const(f);
将失败,因为参数被作为非常量传递。
【讨论】:
您不能在const foo f;
上致电f.modify_const(f)
。你的意思是写void modify_const(foo& f) const
?
@aschepler ups 是的,感谢您发现了那个愚蠢的错误,没有那个const
这个例子毫无意义;)【参考方案5】:
只是表明你永远不安全。 const
限定符不保证值永远不会改变。
试试这样,你可以做非常讨厌的事情:
#include <iostream>
class A
const int i;
void calc(int& j, const int value) const j = value;
public:
A(int _x) : i(_x)
void set1() const calc(*const_cast<int*>(&i), 1);
int getI() const return i;
;
int main()
const A a(3);
std::cout << a.getI() << std::endl;
a.set1();
std::cout << a.getI() << std::endl;
return 0;
【讨论】:
这是UB,你是这个意思吗? @PasserBy 我的意思是const
并不意味着没有办法修改一个值。总有办法的。你不能简单地给某个东西贴上标签 const
并假设它的价值永远不会改变。
在语言的规则范围内,const
对象确实不会改变,改变它的是 UB。我不确定调用这种修改对象的方法是否准确
@PasserBy true,但在这种情况下,我不同意这个定义。该标准提供了工具和实施工作。然而,由于它是不需要的,他们将其标记为“未定义的行为”。在这种情况下,我宁愿将 UB 解释为“不需要的业务”。与此同时,这个const_cast
仍然有效,所以忽略它会将我们的头埋在沙子里。几乎总有一种方法可以修改 const
对象。以上是关于const 方法使用引用修改对象的主要内容,如果未能解决你的问题,请参考以下文章
将const引用/指针传递给类进行存储的首选方法,而不是复制引用的对象