#include <iostream>
class test
{
public:
test() : data_(0) {}
int& f() { return data_; }
const int& f() const { return data_ }
private:
int data_;
};
int main(void)
{
const test rock;
test paper;
/* we can print both */
std::cout << rock.f() << std::endl;
std::cout << paper.f() << std::endl;
/* but we can modify only the non const one */
// rock.f() = 21;
paper.f() = 42;
}
// returns the position of some internal char array in a class Foo
const char& Foo::operator[](std::size_t position) const
{
return arr[position]; // getter
}
// we now define the non-const in terms of the const version
char& Foo::operator[](std::size_t position)
{
return const_cast<char&>( // cast back to non-const
static_cast<const Foo&>(*this)[position] // calls const overload
); // getter/setter
}
8条答案
按热度按时间mi7gmzs61#
但是为什么要在同一个类定义中包含这两个函数呢?
拥有这两种功能,您可以:
const
对象上调用函数,只查看结果。如果只使用第一个,则不能在
const
对象上调用它;如果只使用第二个,则不能使用它来修改它返回引用的对象。编译器如何区分它们?
当在
const
对象上调用函数时(或通过引用或指向const
的指针),它选择const
重载。否则,它选择另一个重载。我相信第二个f()(带const)也可以被非const变量调用。
如果这是唯一的重载,那么它可以。对于这两个重载,将选择非
const
重载。vyswwuz22#
有时候你想为同一个操作提供不同的语义,这取决于它是在
const
对象还是non-const
对象上调用的。让我们以std::string
类为例:在这种情况下,当通过
const
对象调用operator[]
时,您将不允许用户更改字符串的内容。另一方面,在非常量字符串的情况下,你让用户改变字符串的内容。这就是为什么不同的重载。
编译器检查该方法是否在
const
对象或non-const
对象上调用,然后适当地调用该方法。rqqzpn5f3#
第一个不带const的类允许调用者修改对象,该对象通常是其方法被调用的类的成员。
第二个示例中,我们的宿主类处于只读模式,也允许对其成员进行只读访问。
默认情况下,如果constance规则允许,则调用非const版本。
最常见的例子之一是某种集合/数组类型的类。
假设它们被实现了,并且可能是一个模板,或者它们是具体的类型和大小。我将演示const重载。
现在我们可以让某个人使用这个类了。你可能需要设置一个值。
或者你可能只是在阅读它:
所以你可以看到我可以使用这种表示法来设置一个值和读取一个值。就像
operator[]
一样,你可以将这种技术应用于任何方法。cuxqih214#
在Qt框架中有一个很好的例子。
看一下QImage类。
有两个公共函数:
第一个是只读的,第二个是修改扫描线的。
为什么这个区别很重要?因为Qt使用implicit data sharing。这意味着,如果你这样做,QImage不会立即执行深度复制:
相反,仅当调用实际修改两个图像之一的函数(如非常数scanLine)时,才会复制数据。
a0zr77ik5#
函数调用括号后的限定符适用于成员函数的隐藏参数
this
:一个成员函数
void Foo::bar()
是这样的:void bar(Foo *this)
。但是如果Foo对象是const
会发生什么呢?因为
Foo::bar()
有一个Foo *this
参数,而这个参数不允许是const
,所以上面的f.bar();
无法编译。所以我们需要一种方法来限定隐藏的this
参数,C选择的方法是允许这些限定符在函数括号之外。编译器区分这些函数的方式在各个方面都与常规函数重载相同,因为这正是它的本质,尽管语法很奇怪。
此外,
const
不是唯一的限定符。您还可以添加volatile
限定符,并且在C11中,您还可以添加左值和右值引用限定符。我们需要这个函数的两个几乎相同的副本的原因是因为没有直接的方法来提取一个单一的差异:不同的返回类型。如果我们有一个const对象,并且该对象有一个getter,该getter返回对它所包含的内容的引用,该引用需要与整个对象相同。
上面的代码甚至不能编译,因为在
Foo::get_i() const
内部,i
是const,我们不能返回一个非const引用。但是如果允许的话,这是错误的,因为我们不应该修改const对象的成员。所以Foo::get_i() const
需要返回一个const引用到i
。但是我们应该能够修改非常量对象的成员,
所以我们不能只有这个函数。我们需要一个函数,当Foo对象本身不是常量时,它返回一个非常量引用。所以我们根据对象的常量重载函数:
如果函数体更复杂,有一个可能的选择来避免重复:
也就是说,非const重载将其实现委托给const重载,使用const_cast来修复类型。添加const总是安全的。使用const_cast删除const只有在我们确定原始对象不是const时才是安全的。在这种情况下我们确实知道,因为我们知道我们首先将const添加到非const对象。
xwmevbvl6#
它允许您以只读方式访问常量示例数据,同时仍然能够修改非常量示例数据。
vof42yt17#
如前所述,根据调用对象的常量,可以使用
const
和非const
版本的函数。该范例经常与operator[]
一起用于数组。避免代码重复的一种方法(摘自Scott Meyers的书Effective C++)是将const_cast
函数返回到非常量重载中,例如:luaexgnf8#
如果你想让一个函数的const和non-const版本都有相同的实现,而不需要重复代码,你可以在non-const方法上使用const_cast。下面是一个例子:
};