为什么访问器函数必须是const?漏洞在哪里?

时间:2017-07-05 20:11:19

标签: c++ oop const encapsulation getter

当我在学校时,教授们把它敲进了我的脑海,同事们在代码审查中跳了起来,而且几乎所有的C ++教科书都出现了:" accessor" (又名"选择器"或"getter")方法必须标记为const。如果它没有更改或改变数据,请将其标记为const

为什么呢?如何调用访问者来修改私有数据?

在下面的示例中,我设置了一个简单的类和一个访问器。如何getBrand()用于修改私人数据?在我看来,它不能;那么为什么我们需要标记它const

换句话说,我是否正确地说getBrand()在实践中不可能改变私有财产?

示例:

#include <iostream>
#include <string>

class Cheese {
public:
    Cheese(std::string brand):brand_(brand) {}
    std::string getBrand() { return brand_; } // Intentionally not marked const
private:
    std::string brand_;
};

int main() {
    Cheese cheddar("Cabot clothbound");
    std::cout << "Brand: " << cheddar.getBrand() << std::endl;
    return 0;
}

5 个答案:

答案 0 :(得分:10)

实际上非常简单:如果方法不是const,则无法在const个对象上使用它 - 但您确实希望能够使用它。在课堂上,你无法实现

void print_brand(const Cheese& cheese);

(除非你是const-cast,你不应该这样做。)

另外,如果你确实将它设为const,而不是返回你的字符串的副本 - 可能会或可能不会被优化掉,你可以实现:

const std::string& getBrand() const { return brand_; }

返回引用,或者

std::string_view getBrand() const { return brand_; }

不会将您的API“提交”到字符串类(请参阅string_view here;它只是正式添加到C ++ 17中的语言中,但可以{{1最近的编译器。)

答案 1 :(得分:3)

  

漏洞在哪里?

答案是函数名称可以存在,但包含对const的引用的接口不能。

示例:

#include <iostream>
#include <string>

// this function name lies
void i_wont_touch_your_cheese(std::string& s)
{
    // uh-oh - I lied!
    s = "lol, I touched your cheese!";    
}

// this one cannot. The compiler won't allow it
void i_really_wont_touch_your_cheese(const std::string& s)
{
    // compiler error here!
    // cheese is safe
    s = "lol, I touched your cheese!";    
}

int main() {
    auto cheese = std::string("untouched cheese");
    i_wont_touch_your_cheese(cheese);
    std::cout << cheese << std::endl;

    cheese = "more untouched cheese";
    i_really_wont_touch_your_cheese(cheese);
    std::cout << cheese << std::endl;

    return 0;
}

答案 2 :(得分:2)

严格地说你所写的方法并不会改变班级成员。如果您将其标记为const,则编译器会完全阻止它改变成员。但是,让我们在这里深入挖掘一下。

通常,您只需编写一次代码,但多次阅读/查看。标记方法const允许未来的读者查看它,并且立即知道该方法无法更改类状态,因为编译器会捕获它。例如,如果您不小心写了size_t empty() const { return size_ = 0; }(其中size_是成员变量),编译器将捕获您的拼写错误。如果你没有标记方法const,你会有一个微妙的错误。

但更重要的是,const方法只能调用其他const方法。考虑一下,如果你有一个方法将类状态作为输入,做一堆工作并返回一个结果。如果用于执行其工作的getter方法是非常量的,那么长而复杂的方法必须是非常量的,这使得代码理解很多更难。

答案 3 :(得分:1)

关键字const的功能保证使用她,不要更改给定的对象。因此,如果您希望将对象打印到某个函数或特别是对于operator<<外部类,则应该仅使用带有关键字const的方法。

std::ostream &operator<<(std::ostream& str, const Object& obj)
{
    return str << obj.someFunctionConst() << std::endl;
}

有错误的函数(编译错误)

std::ostream &operator<<(std::ostream& str, const Object& obj)
{
     return str << obj.someFunctionWithoutConst() << std::endl;
}

答案 4 :(得分:0)

const不是关于漏洞!从理论上讲,它不会强制执行安全性,安全性或其他任何操作。没有什么能阻止你应用const-cast来删除它或使用关键字mutable来改变某些东西。

const有助于使您的代码更清晰。请参阅Const-Correctness文章并详细解释该概念。

  1. 当您使用const查看某些内容时,您会合理地预期它不会改变状态。声明int Class:foo() const;告诉我foo()不会改变对象的状态,可以从任何地方调用它。
  2. const具有传染性。您使用的越多,您使用它的次数就越多。 bool Class::bar();如果需要致电const,则必须为foo()
  3. 同样适用于反方向。如果您将const &传递给函数void zoo(const Class &cls);,那么您知道zoo()将无法修改cls的状态。