我试图更深入地理解c ++的Math.Combinat.Sets> combine 3 [1 .. 3]
[[1,1,1],[1,1,2],[1,1,3],[1,2,2],[1,2,3],[1,3,3],[2,2,2],[2,2,3],[2,3,3],[3,3,3]]
语义,但我无法完全理解constness保证值得的真正含义。
正如我所看到的,constness保证不会有变异,但请考虑以下(人为的)示例:
const
由于#include <iostream>
#include <optional>
#include <memory>
class A {
public:
int i{0};
void foo() {
i = 42;
};
};
class B {
public:
A *a1;
A a2;
B() {
a1 = &a2;
}
void bar() const {
a1->foo();
}
};
int main() {
B b;
std::cout << b.a2.i << std::endl; // output is 0
b.bar();
std::cout << b.a2.i << std::endl; // output is 42
}
为bar
,人们会认为它不会改变对象const
。但在调用b
之后发生变异。
如果我像这样编写方法b
foo
然后编译器按预期捕获它。
因此,似乎可以相当容易地用指针绕过编译器。我想我的主要问题是,我是如何100%确定void bar() const {
a2.foo();
}
方法不会对它们被调用的对象造成任何突变?或者我对const
完全错误的期望?
为什么c ++允许在const
方法中使用指针调用非const方法?
编辑:
感谢Galik的评论,我现在发现了这个:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4372.html
嗯,这正是我想要的!谢谢! 我发现Yakk的回答也非常有用,所以我会接受他的回答。
答案 0 :(得分:5)
const
告诉来电者&#34;这不应该改变对象&#34;。
const
可以帮助实现者在意外改变状态时产生错误,除非实现者将其丢弃。
const
数据(不是引用,实际数据)为编译器提供保证,修改此数据的任何人都在做未定义的行为;因此,编译器可以自由地假设数据永远不会被修改。
const
库中的 std
对线程安全做出了一定的保证。
所有这些都是const
的使用。
如果某个对象不是const
,则任何人都可以const_cast
离开const
对该对象的引用并进行修改。
如果对象是const
,编译器将无法可靠地诊断您丢弃const
并生成未定义的行为。
如果您将数据标记为mutable
,即使它也标记为const
,它也不会。
std
基于const
提供的保证受到您遵循这些保证后转入std
的类型的限制。
const
在程序员身上没有多少强制执行。它只是试图帮助。
没有语言可以使hostlie程序员友好; C ++中的const
并不会尝试。相反,它试图让更容易来编写const
- 正确的代码而不是写const
- 错误的代码。
答案 1 :(得分:1)
Constness本身并不能保证任何东西。它只消除了特定代码的权利,通过特定的引用来改变对象。它不会剥夺其他代码的权利,通过其他引用来改变同一个对象,就在你的脚下。
答案 2 :(得分:0)
所以看起来人们可以用指针轻松绕过编译器。
确实如此。
我想我的主要问题是,如何或者我是否100%确定const方法不会对它们被调用的对象造成任何变异?
语言保证只在本地意义上。
您的课程间接与以下内容相同:
struct Foo
{
int* ptr;
Foo() : ptr(new int(0)) {};
void bar() const { *ptr = 10; }
};
使用时:
Foo f;
f.bar();
f
的成员变量没有改变,因为指针仍然指向调用f.bar()
之后的位置,就像它在调用之前那样。从这个意义上说,f
没有改变。但是,如果您将f
的“状态”扩展为包含f.ptr
指向的值,那么f
的状态确实会发生变化。该语言不保证不会发生此类变化。
作为设计人员和开发人员,我们的工作是记录我们创建的类型的“const”语义,并提供保留这些语义的函数。
或者我对const有完全错误的期望?
也许
答案 3 :(得分:0)
当一个类方法声明为const
时,这意味着该方法中的隐式this
指针指向一个const
对象,因此该方法不能改变任何成员该对象(除非它们被明确声明为mutable
,在本例中并非如此)。
B
构造函数未声明为const
,因此this
的类型为B*
,即指向非const B
对象的指针。所以a1
和a2
是非const的,a1
被声明为指向非const A
对象的指针,因此编译器允许a1
请指出a2
。
bar()
声明为const
,因此this
的类型为const B*
,即指向const B
对象的指针。
当bar()
拨打a1->foo()
时,a1
因const
指向this
对象而const B
为bar()
,所以{{1} }无法更改a1
以指向其他内容。但是A
指向的a1
对象仍然被a1
的声明视为非常量,而foo()
未被声明为{{1}所以编译器允许调用。但是,编译器无法验证const
实际上是否指向a1
,a2
的成员B
在const
内,所以代码会破坏bar()
合约,并且未定义的行为。
当const
尝试呼叫bar()
时,a2.foo()
由于a2
指向const
对象而this
为const B
,但{ {1}}未声明为foo()
,因此编译器无法通过调用。
const
只是一个安全问题。它不会阻止你使用行为不当的代码射击自己。
答案 4 :(得分:-1)
这是正确的观察。在const限定函数中(在您的示例中为bar
),从该函数访问时,该类的所有数据成员的行为就像它们是const数据成员一样。使用指针,这意味着指针本身是常量,但不它指向的对象。事实上,您的示例可以非常简化为:
int k = 56;
int* const i = &k;
*i = 42;
指向常量对象和常量指针之间存在很大差异,需要理解它,因此首先没有给出的“promises”似乎不会被破坏。