C++ Const方法,在不使用Const_cast的情况下修改*此
以下模式出现在我正在编写的程序中。我希望它不是太做作,但它成功地在const方法C++ Const方法,在不使用Const_cast的情况下修改*此,c++,constants,mutable,const-correctness,C++,Constants,Mutable,Const Correctness,以下模式出现在我正在编写的程序中。我希望它不是太做作,但它成功地在const方法Foo::problem()const中变异了Foo对象,而不使用任何const\u cast或类似方法。基本上,Foo存储对FooOwner的引用,反之亦然,在problem()中,Foo通过对其所有者调用mutate\u Foo()在const方法中修改自身。问题遵循代码 #include "stdafx.h" #include <iostream> using namespace std; cla
Foo::problem()const
中变异了Foo
对象,而不使用任何const\u cast或类似方法。基本上,Foo
存储对FooOwner
的引用,反之亦然,在problem()
中,Foo
通过对其所有者调用mutate\u Foo()
在const方法中修改自身。问题遵循代码
#include "stdafx.h"
#include <iostream>
using namespace std;
class FooOwner;
class Foo {
FooOwner& owner;
int data;
public:
Foo(FooOwner& owner_, int data_)
: owner(owner_),
data(data_)
{
}
void SetData(int data_)
{
data = data_;
}
int Questionable() const; // defined after FooOwner
};
class FooOwner {
Foo* pFoo;
public:
FooOwner()
: pFoo(NULL)
{}
void own(Foo& foo)
{
pFoo = &foo;
}
void mutate_foo()
{
if (pFoo != NULL)
pFoo->SetData(0);
}
};
int Foo::Questionable() const
{
owner.mutate_foo(); // point of interest
return data;
}
int main()
{
FooOwner foo_owner;
Foo foo(foo_owner, 0); // foo keeps reference to foo_owner
foo_owner.own(foo); // foo_owner keeps pointer to foo
cout << foo.Questionable() << endl; // correct?
return 0;
}
您已达到循环依赖。请参阅和yes,即使绕过编译器,修改
const
数据也是无效的。此外,如果不遵守承诺,则会严重削弱编译器的优化能力(请阅读:违犯const
)
如果您知道您将通过呼叫其所有者来修改它,为什么有问题?为什么拥有的对象需要了解所有者?如果您真的需要这样做,那么mutable
就是最好的选择。这就是它的用途——逻辑常量(与严格的位级常量相反)
从我的n3090草案副本中:
9.3.2此指针的
1在非静态(9.3)成员函数体中,关键字this是一个右值prvalue表达式,其
值是为其调用函数的对象的地址。成员函数中的类型
类X的值是X*如果成员函数声明为const,则其类型为const X*,如果成员
函数被声明为volatile,其类型为volatile X*,如果成员函数被声明
const volatile,其类型为const volatile X*
2在常量成员函数中,调用该函数的对象通过常量访问进行访问
路径因此,常量成员函数不应修改对象及其非静态数据成员
[注意我的]
关于UB:
7.1.6.1简历限定词
3指向cv限定类型的指针或引用实际上不需要
指出或参考合格简历
对象,但它被视为
做常数限定访问路径
无法用于修改对象
即使引用的对象是
非常量对象,可以修改
通过其他访问路径。[
注意:cv限定符受
类型系统,因此它们不能
未经铸造而被颠覆(5.2.11)。
-[完注]
4除了任何类别
声明为可变的成员(7.1.1)可以
已修改,任何试图修改
常量对象在其生命周期内(3.8)
导致未定义的行为
const
关键字仅在编译时检查时考虑。C++没有提供任何工具来保护类,不需要任何内存访问,这就是你正在使用的指针/引用。编译器和运行时都无法知道指针是否指向您在某处声明了const的实例
编辑:
简短示例(可能无法编译):
//假设foo有一个成员const int foo::datalength()const{…}
//和只读访问方法const char data(int idx)const{…}
for(int i;i std::coutIMO,您并没有做任何技术上的错误。如果成员是指针,可能更容易理解
class X
{
Y* m_ptr;
void foo() const {
m_ptr = NULL; //illegal
*m_ptr = 42; //legal
}
};
const
使指针常量,而不是指针对象
考虑以下两者之间的区别:
const X* ptr;
X* const ptr; //this is what happens in const member functions
至于引用,由于它们无论如何都无法重置,因此方法上的const
关键字对引用成员没有任何影响
在你的例子中,我没有看到任何const对象,所以你没有做任何坏事,只是在C++中使用一个奇怪的漏洞来验证正确性。
< P>考虑以下内容:
int i = 3;
i
是一个对象,它的类型为int
。它不是cv限定的(不是const
或volatile
,或两者兼而有之。)
现在我们加上:
const int& j = i;
const int* k = &i;
j
是指i
的引用,k
是指i
的指针(从现在开始,我们简单地将“reference-to”和“points-to”组合为“points-to”。)
此时,我们有两个cv限定变量,j
和k
,它们指向非cv限定对象。这在§7.1中提到。5.1/3:
指向cv限定类型的指针或引用不需要实际指向或引用cv限定对象,但会被视为指向或引用cv限定对象;即使引用的对象是非常量对象,并且可以通过其他访问路径进行修改,常量限定访问路径也不能用于修改对象。[注:cv限定符受类型系统支持,因此不使用强制转换(5.2.11)就不能被破坏。]
这意味着编译器必须尊重j
和k
是cv限定对象,即使它们指向非cv限定对象。(因此j=5
和*k=5
是非法的,即使i=5
是合法的。)
我们考虑从<:/P>中删除<代码> const <代码>
const_cast<int&>(j) = 5;
*const_cast<int*>(k) = 5;
这是非常合法的。我们现在认为<代码> i>代码>是:
const int i = 3;
现在我们的代码是什么
const_cast<int&>(j) = 5;
*const_cast<int*>(k) = 5;
请注意,只需执行以下操作:
const_cast<int&>(j);
*const_cast<int*>(k);
bar
上的const
对成员有什么作用?它使对成员的访问通过一种称为cv限定访问路径的方式进行。(它通过将this
的类型从T*const
更改为cv T const*
,其中cv
是函数上的cv限定符。)
那么在代码执行期间,成员类型是什么呢
const int i = 3;
const_cast<int&>(j) = 5;
*const_cast<int*>(k) = 5;
int& j = i; // removed const with const_cast...
int* k = &i; // ...but this is not legal!
j = 5;
*k = 5;
const_cast<int&>(j);
*const_cast<int*>(k);
struct foo
{
foo() :
me(this), self(*this), i(3)
{}
void bar() const
{
me->i = 5;
self.i = 5;
}
foo* me;
foo& self;
int i;
};
// const-pointer-to-non-const, where the pointer points cannot be changed
foo* const me;
// foo& const is ill-formed, cv-qualifiers do nothing to reference types
foo& self;
// same as const int
int const i;
int main()
{
foo f;
f.bar(); // UB?
}
const foo f;
f.bar(); // UB!
struct foo
{
const int& bar() const
{
int* result = /* complicated process to get the resulting int */
return *result;
}
int& bar()
{
// we wouldn't like to copy-paste a complicated process, what can we do?
}
};
int& bar(void)
{
const foo& self = *this; // add const
const int& result = self.bar(); // call const version
return const_cast<int&>(result); // take off const
}
int& bar(void)
{
return const_cast<int&>( // (3) remove const from result
static_cast<const foo&>(*this) // (1) add const to this
.bar() // (2) call const version
);
}
struct foo
{
foo(void) :
i(),
self(*this), me(this),
self_2(*this), me_2(this)
{}
const int& bar() const
{
return i; // always well-formed, always defined
}
int& bar() const
{
// always well-formed, always well-defined
return const_cast<int&>(
static_cast<const foo&>(*this).
bar()
);
}
void baz() const
{
// always ill-formed, i is a const int in baz
i = 5;
// always ill-formed, me is a foo* const in baz
me = 0;
// always ill-formed, me_2 is a const foo* const in baz
me_2 = 0;
// always well-formed, defined if the foo pointed to is non-const
self.i = 5;
me->i = 5;
// always ill-formed, type points to a const (though the object it
// points to may or may not necessarily be const-qualified)
self_2.i = 5;
me_2->i = 5;
// always well-formed, always defined, nothing being modified
// (note: if the result/member was not an int and was a user-defined
// type, if it had its copy-constructor and/or operator= parameter
// as T& instead of const T&, like auto_ptr for example, this would
// be defined if the foo self_2/me_2 points to was non-const
int r = const_cast<foo&>(self_2).i;
r = const_cast<foo* const>(me_2)->i;
// always well-formed, always defined, nothing being modified.
// (same idea behind the non-const bar, only const qualifications
// are being changed, not any objects.)
const_cast<foo&>(self_2);
const_cast<foo* const>(me_2);
// always well-formed, defined if the foo pointed to is non-const
// (note, equivalent to using self and me)
const_cast<foo&>(self_2).i = 5;
const_cast<foo* const>(me_2)->i = 5;
// always well-formed, defined if the foo pointed to is non-const
const_cast<foo&>(*this).i = 5;
const_cast<foo* const>(this)->i = 5;
}
int i;
foo& self;
foo* me;
const foo& self_2;
const foo* me_2;
};
int main()
{
int i = 0;
{
// always well-formed, always defined
int& x = i;
int* y = &i;
const int& z = i;
const int* w = &i;
// always well-formed, always defined
// (note, same as using x and y)
const_cast<int&>(z) = 5;
const_cast<int*>(w) = 5;
}
const int j = 0;
{
// never well-formed, strips cv-qualifications without a cast
int& x = j;
int* y = &j;
// always well-formed, always defined
const int& z = i;
const int* w = &i;
// always well-formed, never defined
// (note, same as using x and y, but those were ill-formed)
const_cast<int&>(z) = 5;
const_cast<int*>(w) = 5;
}
foo x;
x.bar(); // calls non-const, well-formed, always defined
x.bar() = 5; // calls non-const, which calls const, removes const from
// result, and modifies which is defined because the object
// pointed to by the returned reference is non-const,
// because x is non-const.
x.baz(); // well-formed, always defined
const foo y;
y.bar(); // calls const, well-formed, always defined
const_cast<foo&>(y).bar(); // calls non-const, well-formed,
// always defined (nothing being modified)
const_cast<foo&>(y).bar() = 5; // calls non-const, which calls const,
// removes const from result, and
// modifies which is undefined because
// the object pointed to by the returned
// reference is const, because y is const.
y.baz(); // well-formed, always undefined
}
class ComplexProcessor
{
public:
void setInputs( int a, int b );
int getValue() const;
private:
int complexCalculation( int a, int b );
int result;
};
void ComplexProcessor::setInputs( int a, int b ) {
result = complexCalculation( a, b );
}
class ComplexProcessor2 {
public:
void setInputs( int a, int b ) {
a_ = a; b_ = b;
}
int getValue() const {
return complexCalculation( a_, b_ );
}
private:
int complexCalculation( int a, int b );
int a_,b_;
};
class ComplexProcessor3 {
public:
ComplexProcessor3() : cached_(false) {}
void setInputs( int a, int b ) {
a_ = a; b_ = b;
cached_ = false;
}
int getValue() const {
if ( !cached_ ) {
result_ = complexCalculation( a_, b_ );
cached_ = true;
}
return result_;
}
private:
int complexCalculation( int a, int b );
int a_,b_;
// This are not part of the perceivable state:
mutable int result_;
mutable bool cached_;
};
template <typename T>
class SharedValue {
public:
void set( T v ) {
scoped_lock lock(mutex_);
value = v;
}
T get() const {
scoped_lock lock(mutex_);
return value;
}
private:
T value;
mutable mutex mutex_;
};