理解const

时间:2017-08-28 15:23:39

标签: c++ pointers reference c++14 const

如果你想编写一个迭代器,你通常会写

T* operator->() const;

我的问题是理解这个" const"用指针和参考。

例如,您可以编写以下结构:

struct A{
   int* x;

   A(int& x0):x{&x0}{}
   int* ptr() const {return x;}
   int& ref() const {return *x;}
};

你可以这样使用它:

int x = 10;
const A a{x};

int& r = a.ref();
int* p = a.ptr();

*p = 4;
cout << x << endl;   // prints 4

r = 25;
cout << x << endl;  // prints 25

但是为什么这个编译并正常工作(至少用g ++和clang)。为什么呢?

正如我所定义的

const A a{x};

这&#34; a&#34;是const。所以当我打电话时

int* p = a.ptr();

我用const对象调用ptr(),因此内部指针A-&gt; x必须是&#34; int * const&#34;。但我正在返回&#34; int *&#34;没有const。为什么这是正确的?

参考会发生什么?如果我使用&#34; const A&#34;调用A :: ref(),那么该函数返回的类型是什么?像&#34; int&amp ;;常量&#34; ??? &lt; ---我想这与&#34; int&amp;&#34;相同。

感谢您的帮助。

3 个答案:

答案 0 :(得分:10)

按位常量和逻辑常量之间存在差异。

如果您拥有const A,则无法修改其int*成员。但修改成员本身({em> <{em> int x指向)和修改成员(int的值之间存在差异那x指向)。这些是const的不同类型。在最简单的情况下:

struct Ptr { int* p; };

int i = 42;
const Ptr ptr{&i};
*ptr.p = 57; 

ptr.p仍然指向i,没有任何变化,因此const机制被强制执行。但它不是逻辑const,因为您仍然通过const对象更改了某些内容。尽管如此,该语言并没有强制执行。作为图书馆作家,这取决于你。

如果你想传播 const - ness,你只需提供一个逻辑const的接口:

int const* ptr() const {return x;}
int const& ref() const {return *x;}
//  ^^^^^

现在,用户无法通过const A按位进行修改(无法改变x指向的内容)和逻辑上的修改(无法改变该值指出要么)。

答案 1 :(得分:3)

  

但是为什么这个编译并正常工作(至少用g ++和clang)。为什么呢?

因为程序结构良好并且已经定义了行为。没有违反Const正确性。

  

我用const对象调用ptr(),因此内部指针A-> x必须是“int * const”。但是我返回一个没有const的“int *”。为什么这是正确的?

因为完成const对象的复制是完全可以的。那些副本不一定是常量。复制对象不会对原始对象进行修改(假设没有用户定义的复制构造函数可以执行愚蠢的操作)。

  

参考会发生什么?如果我用“const A”调用A :: ref(),这个函数返回的类型是什么?

Date X DATE 0 26-Aug-17 10:11:29 95.617378 NaT 1 26-Aug-17 11:11:29 93.617378 NaT 2 26-Aug-17 12:11:29 91.617378 NaT 3 26-Aug-17 13:11:29 90.000000 NaT 始终返回int& ref()。就像int&总是返回int* ptr()

  

类似“int&amp; const”???

没有int*之类的东西。引用不能有顶级限定符(永远不能重新赋值)。

答案 2 :(得分:2)

struct A中,当你创建它的实例const时,你使指针保持不变,但这不会自动使指向对象保持常量

声明类似const A a(ref);的内容基本上等同于调用以下代码:

struct A_const {
    int * const x;

    A(int& x0):x{&x0}{}
    int* ptr() const {return x;}
    int& ref() const {return *x;}
};

如果你记得你的指针规则,这意味着x是一个常量指针,这意味着它不能指向别的东西(它在功能上类似于引用,但可以是null ),但关键的是,它指向的int不是常数,这意味着没有什么可以阻止你写这样的东西:

int val = 17;
const A a(val);
*(a.val) = 19; //Totally valid, compiles, and behaves as expected!
int val2 = 13;
//a.val = &val2; //Invalid, will invoke compile-time error

这也是std::unique_ptr<int>std::unique_ptr<const int>代表不同对象的原因。

如果希望指向对象不能在const对象上修改,则需要在代码本身中强制执行。由于函数可以根据源对象是否为const而重载,这非常简单:

struct A {
    int * x;

    A(int& x0):x{&x0}{}
    int * ptr() {return x;}
    int & ref() {return *x;}
    int const* ptr() const {return x;}
    int const& ref() const {return *x;}
};

int val = 17;
A a(val);
a.ref() = 19;//Okay.
*a.ptr() = 4;//Okay.

const A b(val);
b.ref() = 13;//Compile error
*b.ptr() = 17;//Compile error