C++ const问题

8
如果我这样做:
// In header 
class Foo {
void foo(bar*);
};

// In cpp
void Foo::foo(bar* const pBar) {
//Stuff
}

编译器并不会抱怨Foo::foo的签名不匹配。但是,如果我有以下代码:
void foo(const bar*); //In header
void Foo::foo(bar*) {} //In cpp

代码无法编译。
发生了什么事情? 我正在使用gcc 4.1.x。

你的第二个例子中,你是不是想把const放到*的另一边?有些人回答时解释了这种差别的含义,而另一些人则解释了在cpp文件还是h文件中const的区别。 - Scott Langham
你应该选择一个答案来回答你的问题,或者更新问题描述以便找到你需要的答案。 - Douglas Mayle
7个回答

15

在第一个例子中,您向编译器承诺不会编辑变量,但未向类的其他用户承诺。

在第二个例子中,您向类的其他用户承诺不会编辑他们的变量,但未能遵守该承诺。

我还应该指出,存在明显的区别。

bar* const variable

const bar* variable

const bar* const variable
在第一种形式中,指针将永远不会改变,但您可以编辑所指向的对象。在第二种形式中,您可以编辑指针(将其指向另一个对象),但永远无法编辑它所指向的变量。在最后一种形式中,您既不会编辑指针,也不会编辑它所指向的对象。参考 为了更清晰地阐明所提出的问题,您始终可以承诺比少更多的 const。给定一个类:
class Foo {
    void func1 (int x);
    void func2 (int *x);
}
您可以编译以下实现代码:
Foo::func1(const int x) {}
Foo::func2(const int *x) {}
或:
Foo::func1(const int x) {}
Foo::func2(const int* const x) {}

没有任何问题。你已经告诉你的用户,你可能会编辑他们的变量。在你的实现中,你告诉编译器这个特定的实现不会编辑那些变量,即使你告诉用户可能会。你没有违背对用户的承诺,所以代码能够编译。


我认为你的理论不成立:1)如果你声明“int *”,并实现“const int *”,就无法编译。2)如果你按值传递,你可以声明为常数,但在按值传递时执行没有意义。 - Dusty Campbell
是的,我同意你是错的。void f(int const*);与void f(int *);不同。我认为你想写的是void f(int * const);它与void f(int *);具有相同的签名。你应该更改你的例子,因为它可能会让初学者感到困惑。 - Johannes Schaub - litb

6
请参考此问题此问题此问题
基本上,const 只是表示函数不会修改指针的值。指针内容不是 const,与头文件签名相同。

2

第一个示例中的const关键字是没有意义的。您是在说您不打算更改指针。然而,该指针是按值传递的,因此无论您是否更改它都无关紧要;它不会影响调用者。同样,您也可以这样做:

// In header 
class Foo {
void foo( int b );
};

// In cpp
void Foo::foo( const int b ) {
//Stuff
}

你甚至可以这样做:

// In header 
class Foo {
void foo( const int b );
};

// In cpp
void Foo::foo( int b ) {
//Stuff
}

由于int是按值传递的,因此constness并不重要。

在第二个例子中,您说您的函数接受一个指向一种类型的指针,但实际上它接受一个指向另一种类型的指针,因此它失败了。


0
在前者中,const 不会影响接口,只会影响实现。你告诉编译器,“我不会在这个函数内改变 bar* 的值”。你仍然可以更改指针所指向的内容。在后者中,你告诉编译器(以及所有调用者),你不会改变 bar* 所指向的 bar 结构体。

我认为你在最后一句话中的意思是“你不会修改bar指向的那个bar”。 “更改bar指向的内容”可能被理解为“给bar*赋一个新值”,当然,在第二个例子中这是可以的。 - Steve Jessop
谢谢你修复了这个问题。代词是不清晰的大敌。 - Greg Hewgill

0

它可能不会太关心void Foo::foo(bar* const pBar),因为你如何处理指针本身(const或非const)在例程之外没有任何影响。 C语言规则指出,无论哪种方式,对pBar的更改都不会传播到foo之外。

然而,如果是(const bar* pBar),那就有所不同了,因为这意味着编译器不允许调用者传递指向非const对象的指针。


0

所以在第二个const中:

void Foo::foo(const bar* const);

not 不是方法签名的一部分吗?


1
你在.h文件中声明一个方法,在.cpp文件中定义它。声明中的所有内容都是签名的一部分(包括常量),在定义中,只要不破坏声明的语义,就可以更精细地定义参数类型。 - Scott Langham
@mamin:正确。例如,如果您声明(但不定义)一个函数“void foo(const int* const a)”,然后调用它,您会得到链接器错误。GCC报告缺失的签名是“foo(int const*)”。 - Steve Jessop
简单来说,如果你添加或删除const,它将不会成为函数签名的一部分。因此,void foo(intconst); 和 void foo(int); 确实具有相同的签名。但是,void foo(int const*); 和 void foo(int*); 具有不同的签名:参数类型没有添加const,而是指向的类型添加了const。 - Johannes Schaub - litb

0

如果使用指针以外的变量类型,这将更容易理解。例如,您可以有以下函数声明:

void foo( int i );

定义可以如下所示:

void foo( const int i ) { ... }

变量'i'在定义时是否为const是一项实现细节。这对该函数的客户端没有影响。


我不明白stackoverflow用户投票的方式。最高得票数+15的答案,在结尾处有错误陈述,但似乎没有修正的打算(人们3个月前已经告诉他了),而得分-1的答案则解释得更好(在我看来)。为了公平起见,我会给这个答案点赞。 - Johannes Schaub - litb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接