一个常量成员函数和一个非常量成员函数有什么区别?

16

我对下面这种常量版本和非常量版本的成员函数感到非常困惑:

value_type& top() { return this.item }
const value_type& top() const { return this.item }

这两个函数有什么区别?在什么情况下会使用它们?


1
你应该考虑阅读《The Definitive C++ Book Guide and List》中列出的优秀入门书籍之一。 - James McNellis
1
吹毛求疵:mutable 是 C++ 的一个关键字,具有自己的含义,并不仅仅是 const 的相反。你的问题更清晰地表述应该是:“const 成员函数和非 const 成员函数之间有什么区别?” - Josh Kelley
1
@Josh - 你提到的明确消除歧义的观点很好,但我认为在编程语言中,“mutable”是足够常见的描述,指的是“可以修改的某个东西”。 - Stephen
@Josh:我同意并已相应更新了问题。如果有人对此感到冒犯,请随意回滚 :) - fredoverflow
@Josh:你说得对。mutable 不像 non-const 那么精确。 - Yongwei Xing
5个回答

17
简而言之,它们用于为您的程序添加“常量正确性”。
value_type& top() { return this.item }

这用于提供对item的可变访问。它被用于允许您修改容器中的元素。

例如:

c.top().set_property(5);  // OK - sets a property of 'item'
cout << c.top().get_property();  // OK - gets a property of 'item'

这个模式的一个常见示例是使用 vector::operator[int index] 返回对元素的可变访问。

std::vector<int> v(5);
v[0] = 1;  // Returns operator[] returns int&.

另一方面:
const value_type& top() const { return this.item }

这个用于提供对 itemconst 访问。它比之前的版本更加严格,但是有一个优点 - 你可以在 const 对象上调用它。

void Foo(const Container &c) {
   c.top();  // Since 'c' is const, you cannot modify it... so the const top is called.
   c.top().set_property(5);  // compile error can't modify const 'item'.
   c.top().get_property();   // OK, const access on 'item'. 
}

接下来以向量为例:

const std::vector<int> v(5, 2);
v[0] = 5;  // compile error, can't mutate a const vector.
std::cout << v[1];  // OK, const access to the vector.

6

如果调用const限定的成员函数,该成员函数将被调用,而对象必须是const限定的。

如果调用非const限定的成员函数,则该成员函数将被调用,而对象不需要是const限定的。

例如:

MyStack s;
s.top(); // calls non-const member function

const MyStack t;
t.top(); // calls const member function

请注意,当在对象的引用或指针上调用成员函数时,相同的规则适用:如果指针或引用指向const对象,则将调用const成员函数;否则将调用非const成员函数。

如果有一个成员函数像value_type& top() const { return this.item },非const对象能调用它吗?如果添加const像函数foo() const,这意味着它会改变当前对象,对吗?如果是这样,它应该由非const或const对象调用。对吗? - Yongwei Xing
如果一个对象不是const,并且成员函数有const和非const两种重载形式(并且这些重载形式在其他方面相同),那么将调用非const成员函数。您可以通过将对象强制转换为const引用,然后在其上调用成员函数来“强制”调用const成员函数。const成员函数不能修改任何非可变成员对象,也不能调用任何非const成员函数。 - James McNellis
@Yongwei Xing,补充一下James的回答,如果你只提供一个“const”重载,它将被const和非const对象使用。只有当你同时拥有const和非const重载时,它们才会使用不同的版本。通常,如果某个东西可以声明为“const”,那么它应该是这样的...非const对象可以调用const和非const函数,而const对象只能调用const限定的成员函数。 - Michael Aaron Safyan

4

如果您有

class Foo
{
    value_type& top() { return this.item }
    const value_type& top() const { return this.item }
}

如果您拥有
Foo foo;
const Foo cfoo;

调用 top() 时返回的类型如下:
value_type& bar = foo.top();
const value_type& cbar = cfoo.top();

换句话说,如果您有一个类的常量实例,则选择调用const版本的函数作为重载。
这个特定情况的原因是,您可以从类的常量实例中提供成员的引用(如此例中的item),并确保它们也是常量 - 因此不可修改,从而保留了它们来自的实例的常量性。

0
当成员函数声明为const时,隐式传递给函数的this指针参数会被声明为指向常量对象的指针类型。这使得该函数可以使用常量对象实例进行调用。
value_type& top();    // this function cannot be called using a `const` object
const value_type& top() const; // this function can be called on a `const` object

0

value_type& top() { return this.item; } 确保调用对象的数据成员可以被修改或者返回值可以被修改。

value_type& top() const { return this.item; } 确保调用对象的数据成员不能被修改,但是返回值可以被修改。例如,如果我执行 value_type item_of_x = x.top();item_of_x 可以被修改,但是 x 不能被修改。否则,编译器会报错(例如在函数体内有 this.item = someValue; 的代码)。

const value_type& top() { return this.item; } 确保调用对象的数据成员可以被修改,但是返回值不能被修改。这与上面讨论的相反:如果我执行 const value_type item_of_x = x.top();item_of_x 不能被修改,但是 x 可以被修改。 注意 value_type item_of_x = x.top(); 仍然允许修改 item_of_x,因为 item_of_x 现在是非 const 的。

const value_type& top() const { return this.item; } 确保调用对象的数据成员和返回值都不能被修改。


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