如果方法没有改变参数,为什么我不能把它作为const传递?

时间:2011-11-18 22:42:49

标签: c++ const

我是一名学生,但仍然没有得到const参数。我理解为什么这不起作用:

#include <stddef.h>

struct Node {

    int value;
    Node* next;
};

Node* cons(const int value, const Node * next) {

    Node * tmp = new Node();
    tmp->value = value;
    tmp->next = next;

    return tmp;
}

int main () {

    const Node * k;
    k = cons(1, NULL);

    Node * p;

    p = cons(2, k);

}

这是因为我通过给出p的地址来“抛弃”k的常量。

我通过标记参数“const”的意图是说我的方法不会直接更改节点。就好像我要传入(Node * next)一样,我觉得不能保证我的方法不会取消引用该指针并弄乱节点。也许这是愚蠢的,因为之后无法保证以后在const节点上我传递的指针不会通过新的指针来改变它。看起来很奇怪:在这个方法中,我所做的就是将一个新指针指向“下一个” - 我从未接触到下一个,只是指向 - 但这足以打破常量。

也许我只是低估了“常规”承诺的力量。

谢谢!

4 个答案:

答案 0 :(得分:9)

问题是您的next参数的类型为const Node *(读取:指向常量Node对象的指针),而Node::next字段的类型为Node * (指向非const Node对象的指针)。

所以在做的时候

tmp->next = next;

您尝试使编译器获取指向const Node对象的指针,并将其指定给指向非const Node的指针。如果这样做有效,那么这将是一种规避常量的简单方法,因为突然之后人们可以修改next参数,只需解除引用tmp->next

答案 1 :(得分:3)

  

也许我只是低估了“常规”承诺的力量。

那么你做出的承诺是什么?该函数采用指向Node的指针的副本,并承诺不允许对Node进行任何修改。承诺不是关于指针,而是Node * const(并且相当无用,因为它是通过值传递的,并且该级别的const从签名中删除),但关于指针, Node

为了提供一个现实生活中的例子,考虑到一个朋友在你度假期间提供给你的植物浇水,承诺(通常暗示)他不会从你的房子里偷走任何东西。现在你复制一把钥匙并交给你的朋友,他自己制作钥匙的副本,并把它交给一个从未承诺不会抢劫你的第三方。你认为你的朋友违背了他的诺言吗?他的承诺有多强大?

上面的代码具有完全相同的模式,cons承诺你可以给它一个指向Node的指针的副本,并且它不会允许它被修改,但是尽快当你输入代码时,他将指针复制到一个不同的指针,该指针不对Node做任何承诺,然后将副本交给可能感兴趣的人。

答案 2 :(得分:2)

如果T是任何类型,则有两个相关的指针类型,T *const T *

通过获取T类型的某个对象的“地址”,您可以获得本质上的指针。指针的类型取决于对象的常量:

T x;
const T y;

T * p1       = &x; // OK, &x is T*
// T * p2    = &y; // Error!
const T * q1 = &y; // OK, &y is const T *
const T * q2 = &x; // also fine

因此,如果我们有一个可变对象,指向它的指针自然是一个指向可变的指针。但是,如果我们只有一个常量对象,那么指针只能是一个指向常量的指针。因为我们总是可以将可变对象视为常量对象(只是不要触摸),所以指向变量的指针总是可以转换为指向const的指针,就像q2的情况一样。 / p>

这就是k发生的事情:它是const T *T = Node},我们正在为其分配T *的值,这很好因为我们只是忽略了如果我们喜欢我们就可以改变T的事实。

[在现实生活中,我们通常有常量的引用而不是平坦的常量对象,但你可以将它们视为同样的东西(因为引用只是一个别名< / em>用于对象并且在功能上与对象本身相同)。]

现在你可能会混淆所有这些const s与对象本身的常量:正如我们之前有xy,一个是可变的和一个常量,我们可以申请与指针类型本身相同的逻辑:让我们定义P = T *Q = const T *。然后是上面的代码,我们所有的对象都是 mutable 对象P p1, p2; Q q1, q2;。也就是说,我们总是更改指向其他地方的任何指针:q2 = &z;。没问题。这就是你在重新分配时对k做的事情。

当然,我们也可以声明这些指针的常量版本:const P p3 = &x; const Q q3 = &y;。这些不能重新分配(毕竟是常量)。如果你按照T的方式解决这个问题,就像人们通常那样,它会有点响亮:

T       * p3 const = &x;
const T * q3 const = &y;

我希望这不会造成比解决更多的混乱!

答案 3 :(得分:1)

  

也许这是愚蠢的,因为当时无法保证以后在const节点上我传递的指针不会通过新的指针来改变它。看起来很奇怪:在这个方法中,我所做的就是将一个新指针指向“下一个” - 我从未接触到下一个,只是指向 - 但这足以打破常量。

这不仅仅是你指出的,而是你用非const指针指向的。如果您将Node::entry设为const Node*,则事情应该有效。

当然,这限制了您在操作列表时可以执行的操作。

在C ++中实际上有两种不同形式的const:就你现在所关注的而言,“诚实地真实const”和“const。”大多数时候你会处理第二种,就像在例子中一样。但是,您的程序需要正确使用const两种形式才能const更正:

int main()
{
    const Node n = { 5, NULL };
    Node* p = cons(6, &n);
}

const(或指向n的指针)上抛弃n是未定义的行为,而cons必须尊重这一点。我还要提一下,我相信你的一部分假设就是你认识到你的函数可以修改const Node*,但认为由于麻烦的赋值是方法的最后一行编译器应该认识到实际上做错了什么。我不知道C ++中的任何规则都说“做X是违法的,除非你把它作为函数的最后一行。”无论是在函数中的第一个还是最后一个,都不允许赋值。

您或许可以获得所需的设计,并且const也是正确的。例如,您 允许重载cons以获取const Node*Node*并构建const或非{{ {1}}相应的元素。如果我更了解你想做什么,我可能会对如何到达那里有更好的答案。