【问题标题】:C++: Const correctness and pointer argumentsC++:常量正确性和指针参数
【发布时间】:2012-02-07 03:41:00
【问题描述】:

我知道 const 指针可以通过几种方式声明:

const int * intPtr1; // Declares a pointer that cannot be changed.
int * const intPtr2; // Declares a pointer whose contents cannot be changed.

// EDIT: THE ABOVE CLAIMS ARE INCORRECT, PLEASE READ THE ANSWERS.

但是在函数参数的上下文中相同的原则呢?

我认为以下是多余的:

void someFunc1(const int * arg);
void someFunc2(int * arg);

由于 someFunc 1 和 2 对指针本身进行了值传递,因此 someFunc1 在给定的函数调用中不可能更改原始指针的值。举例说明:

int i = 5;
int * iPtr = &i;

someFunc1(iPtr); // The value of iPtr is copied in and thus cannot be changed by someFunc1.

如果这些都是真的,那么声明一个具有 'const int * ptr' 类型 arg 的函数是没有意义的,对吗?

【问题讨论】:

  • 您的声明方式错误。 "const int *" 是指向不可更改 int 的可更改指针。 "int * const" 是指向可变 int 的不可更改指针。
  • 编辑了问题,以便触发快乐的谷歌用户不会误会。我留下了错误,所以答案仍然有那个上下文。

标签: c++ function pointers constants


【解决方案1】:

你倒过来了:

const int * intPtr1; // Declares a pointer whose contents cannot be changed.
int * const intPtr2; // Declares a pointer that cannot be changed.

下面的const确实是不必要的,也没有理由把它放在函数中声明

void someFunc1(int * const arg);

但是,您可能希望将其放入函数实现,原因与您可能希望声明局部变量(或其他任何东西)const 的原因相同 - 实现可能更容易当您知道某些事情不会改变时,请遵循。无论它是否在函数的任何其他声明中声明为 const,您都可以这样做。

【讨论】:

  • 现在我看起来像个傻瓜......感谢简洁的解释并确认其中一个定义中确实存在不必要的 const!
  • const int * intPtr1int const * intPtr1;有什么区别吗?
  • @yes123:完全没有区别。 const 限定它之前的东西,除非它在开头,在这种情况下它限定第一件事。所以在这两种情况下,它都限定了int,而不是指针。 int * const 将限定指针。
  • @MikeSeymour:哇,这么清楚,我想知道有人问这个问题吗? (我发现问题可以通过您的评论来解决。)
【解决方案2】:

它不是针对调用者的,而是针对someFunc1 中的代码。这样someFunc1 中的任何代码都不会意外更改它。喜欢

void someFunc1(int *arg) {
  int i = 9;
  arg = &i;  // here is the issue
  int j = *arg;
}

让我们做一些案例研究:

1) 只是将指向的值设为 const

void someFunc1(const int * arg) {
int i = 9;
*arg = i; // <- compiler error as pointed value is const
}

2) 只是将指针设为 const

void someFunc1(int * const arg) {
int i = 9;
arg = &i; // <- compiler error as pointer is const
}

3) 如果涉及的变量可以是 const,则使用 const 的正确方法:

void someFunc1(const int * const arg) {
    int i = 9;
    *arg = i; // <- compiler error as pointed value is const
    arg = &i; // <- compiler error as pointer is const
}

这应该可以消除所有疑虑。所以我已经提到它是针对函数代码而不是针对调用者的,你应该使用我上面提到的 3 种情况中最严格的一种。

编辑:

  • 即使在函数声明中,声明const 也是一个好习惯。这不仅会增加可读性,而且调用者也会意识到合约,并对参数的不变性更有信心。 (这是必需的,因为您通常会共享您的头文件,因此调用者可能没有您的实现 c/cpp 文件)
  • 如果声明和定义同步,即使编译器也能更好地指出。

【讨论】:

  • 但这并不能解释你为什么要写void someFunc1(const int * arg);。请注意,将 someFunc1 预先声明为 void someFunc1(int * arg); 仍然可以让您根据需要将其定义为 void someFunc1(int * const arg) { ... };参数变量本身的const-ness 不会影响实际的函数签名。
  • @ruakh 请查看更新后的答案。这应该是您关心的问题。
  • 抱歉,不,您更新的答案仍然没有回答问题。 (有关正确答案,请参见上面 Mike Seymour 的回答。)
  • "但是,您可能希望将其放入函数实现中,原因与您可能希望声明一个局部变量(或其他任何东西) const" = "我已经提到它的意思是相同的对于功能代码而不是调用者“......无论如何,这点很清楚,这就是底线......
  • 我想我只是对真正回答问题的答案有一种奇怪的偏好!
【解决方案3】:

你的逻辑是错误的。您应该向后读取类型,因此 const int * 是指向 const int 的指针,int * constconst 指向 int 的指针。

例子:

void foo() {
    int a = 0;
    int b = 0;

    int * const ptrA = &a;
    *ptrA = 1;
    ptrA = &b; ///< Error

    const int * ptrB = &a;
    *ptrB = 1; ///< Error
    ptrB = &b;

    const int * const ptrC = &a;
    *ptrC = 1; ///< Error
    ptrC = &a; ///< Error
}

要详细说明并说明为什么您希望函数参数为const int *,您可能需要向调用者表明他们必须传入int,因为您作为函数想要更改值。以这段代码为例:

void someFunc1(const int * arg) {
    // Can't change *arg in here
}

void someFunc2(int * arg) {
    *arg = 5;
}

void foo() {
    int a = 0;
    someFunc1(&a);
    someFunc2(&a);

    const int b = 0;
    someFunc1(&b);
    someFunc2(&b); ///< *** Error here. Must pass in an int not a const int.
}

【讨论】:

    【解决方案4】:

    是的,你是对的(忽略你弄错了它们的事实)- 采用非引用 const 参数是没有意义的。此外,返回非引用 const 值没有任何意义。

    【讨论】:

    • 非引用 const 返回值甚至是有害的。
    • @Xeo:你能详细说明一下吗?我根本不知道它们有什么作用!
    • @ruakh:见When does a const return type interfere with template instantiation? 这是一个相当小的问题。
    • @ruakh:移动语义为一个。 T const&amp;&amp; 不能被移出,即使你知道它是一个临时的,很快就会被销毁。
    • @ruakh:那篇文章是十多年前写的,所以是的,远在移动语义建立之前。
    【解决方案5】:

    你搞错了:

    const int * intPtr1; // Declares a pointer whose contents cannot be changed.
    int * const intPtr2; // Declares a pointer that cannot be changed.
    

    一般来说,在编写稍有不同的表达式时更容易推理 constness:const int*int const * 的类型相同。在这种表示法中,规则更加清晰,const 始终适用于它之前的类型,因此:

    int const * intPtr1; // Declares a pointer to const int.
    int * const intPtr2; // Declares a const pointer to int.
    int const * * const * complexPtr; // A pointer to const pointer to pointer to const int
    

    当类型以const 开头时,const 的处理方式就像它写在第一个类型之后一样,因此const T* 变为T const *

    void someFunc2(int * arg);
    

    因此不是多余的,因为someFunc2 可能会更改arg 的内容,而someFunc1 可能不会。 void someFunc3(int * const arg); 虽然是多余的(并且模棱两可)

    【讨论】:

    • 常用技巧:从右到左读取任何类似 c 的声明:int const * intPtr1 --> intPtr1 是一个指向 const(只读)整数的指针
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-30
    • 2011-03-27
    • 1970-01-01
    • 2021-10-02
    • 2020-10-23
    相关资源
    最近更新 更多