c ++的`const`承诺有什么用?

时间:2018-03-14 19:39:11

标签: c++ const

我试图更深入地理解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的回答也非常有用,所以我会接受他的回答。

5 个答案:

答案 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对象的指针。所以a1a2是非const的,a1被声明为指向非const A对象的指针,因此编译器允许a1请指出a2

bar()声明为const,因此this的类型为const B*,即指向const B对象的指针。

bar()拨打a1->foo()时,a1const指向this对象而const Bbar(),所以{{1} }无法更改a1以指向其他内容。但是A指向的a1对象仍然被a1的声明视为非常量,而foo()未被声明为{{1}所以编译器允许调用。但是,编译器无法验证const实际上是否指向a1a2的成员Bconst内,所以代码会破坏bar()合约,并且未定义的行为

const尝试呼叫bar()时,a2.foo()由于a2指向const对象而thisconst B,但{ {1}}未声明为foo(),因此编译器无法通过调用。

对于行为良好的代码,

const只是一个安全问题。它不会阻止你使用行为不当的代码射击自己。

答案 4 :(得分:-1)

这是正确的观察。在const限定函数中(在您的示例中为bar),从该函数访问时,该类的所有数据成员的行为就像它们是const数据成员一样。使用指针,这意味着指针本身是常量,但它指向的对象。事实上,您的示例可以非常简化为:

int k = 56;
int* const i = &k;
*i = 42;

指向常量对象和常量指针之间存在很大差异,需要理解它,因此首先没有给出的“promises”似乎不会被破坏。