据我所知,在类中创建常量函数对于读/写编译器优化非常有用。
类中的常量函数意味着类成员在执行函数期间将保持不变。 但是,你可以通过const转换隐式参数来绕过这个(ofc这是一个非常糟糕的做法)。
我的问题如下:
以下代码可能导致哪些陷阱(特别是在与线程同步无关的性能方面)?
int myClass::getSomething() const
{
myClass* writableThis = const_cast<myClass*>(this);
writableThis->m_nMemberInt++;
...
return m_nSomeOtherUnchangedMember;
}
另一个相关问题:
行为编译器/ platform / os是否具体?
如果有人能够在编译/执行这样的代码时解释引擎的魔力,我也非常感激(我推测CPU正在根据函数的事实进行无序优化) const,并且在实际执行期间不尊重它应该有一些副作用)。
编辑:
感谢您为我澄清这一点。经过进一步的研究,所有收到的答案都是正确的,但我只接受一个:)。
关于仅用于语法核心的const限定符,我相信这个答案是正确和错误的,说明这个(imho)的正确方法是它主要用于语法核心(在非常有限的数量中)场景它可以产生不同/更好的代码)。参考文献:SO Related question,related article
答案 0 :(得分:6)
const_cast<T>(this)
技巧可能不安全,因为你的成员函数的用户可能会遇到未定义的行为,而不会在他们身边做任何错误。
问题是只有当你以非const对象开始时才允许抛弃const-ness。如果你的对象是常量,那么抛弃其常量并使用结果指针改变对象状态的函数会触发未定义的行为:
struct Test {
int n;
Test() : n(0) {}
void potentiallyUndefinedBehavior() const {
Test *wrong = const_cast<Test*>(this);
wrong->n++;
}
};
int main() {
Test t1;
// This call is OK, because t1 is non-const
t1.potentiallyUndefinedBehavior();
const Test t2;
// This triggers undefined behavior, because t2 is const
t2.potentiallyUndefinedBehavior();
return 0;
}
使用const_cast<T>(this)
的技巧是为了使用const
限定符缓存成员函数内的值而发明的。但是,它不再有用,因为C ++为这类事物添加了一个特殊的关键字:通过标记成员mutable
,您可以在const
内使该成员可写 - 合格的方法:
struct Test {
mutable int n;
Test() : n(0) {}
void wellDefinedBehavior() const {
n++;
}
};
现在,无论上下文如何,const
成员函数都不会触发未定义的行为。
答案 1 :(得分:2)
CPU对const
(C ++关键字)一无所知。当编译器将C ++代码转换为汇编时,剩下的就不多了。
当然,由于const
关键字,生成的代码完全的可能性很小。例如,某些const
的{{1}}版本可能会按值返回operator[]
个对象,而非const版本必须返回T
。 CPU甚至不知道它所处的功能,甚至不知道功能是否存在。
答案 2 :(得分:2)
我的回答是使用存储类mutable
来处理需要在const
方法中修改的任何内容。
它内置于语言中,因此有几个好处。它对const方法修改数据成员的方式有了更严格的控制。其他开发人员会知道这些数据成员将在const方法中发生变化。如果有任何编译器优化,编译器将知道做正确的事情。
class myClass {
private:
int m_nSomeOtherUnchangedMember;
mutable int m_nMemberInt;
…
public:
int getSomething() const;
…
};
int myClass::getSomething() const
{
m_nMemberInt++;
…
return m_nSomeOtherUnchangedMember;
}
答案 3 :(得分:2)
据我所知,在类中创建常量函数对于读/写编译器优化非常有用。
没有。我们使用const方法来强制执行语义保证,而不是允许优化(除了避免副本之外)。
以下代码会导致什么陷阱
首先,它可以打破程序语义。
例如,std::map
个节点存储std::pair<const Key, T>
,因为密钥在插入后不应该发生变异。如果密钥更改值,则映射排序不变量不正确,后续的查找/插入/重新平衡操作将出现异常。
如果你在这个const键上调用const限定方法,并且该方法以影响比较方式的方式更改Key,那么你狡猾地破坏了地图。
其次,它可以杀死你的程序。如果const对象覆盖在真正的只读地址范围内,或者在只读初始化数据段中有静态初始化的const对象,则写入它将导致某种保护错误
< / LI> 醇>答案 4 :(得分:0)
正如其他人所说,const-correctness被设计为程序员的帮助,而不是优化器的帮助。你应该记住4件事:
更具体地说,优化器完全忽略了引用或方法的常量,因为const
在这种情况下并不能真正意味着你在想什么。
对象的const引用并不意味着例如在执行方法期间对象将保持不变。考虑例如:
struct MyObject {
int x;
void foo() const {
printf("%i\n", x);
char *p = new char[10];
printf("%i\n", x);
delete[] p;
}
};
编译器不能假设x
成员在对printf
的两次调用之间没有变异。原因是std::operator new
全局分配器可能已经被重载,并且代码可能具有到该实例的常规非const指针。因此,全局分配器在执行x
期间更改foo
是完全合法的。编译器无法知道这不会发生(全局分配器可能在另一个中重载
编制单位)。
调用任何未知代码(即基本上任何非内联函数)都可以改变对象的任何部分,或者是const方法。 const方法只是意味着你不能使用this
来改变对象,而不是对象是常量。
如果const正确性确实对程序员有帮助,那么另一个问题就是我个人有一个非常异端的观点,但这是另一个故事......