Why do accessor functions have to be const? Where is the vulnerability?

The professors got it in my head when I was in school, my colleagues jumped down my throat to read it in code reviews, and this is in almost every C ++ textbook: "accessor" (aka "selector" or "getter" ) methods should be marked const

. If it does not change or change the data, check it const

.

Why? How can calling a caller change personal information?

In the following example, I have set up a simple class and one accessor. How getBrand()

can you use to change personal data? In my eyes it cannot; so why should we celebrate it const

?

In other words, can I correctly say that in practice it is impossible to use getBrand()

to change private property?

Example:

#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;
}

      

+3


source to share


5 answers


It's actually very simple: if the method is not const, you can't use it on objects const

, but you want to use it. With your class, you cannot implement

void print_brand(const Cheese& cheese);

      

(unless you are creating a const-cast, which you shouldn't).

Also, if you make it const instead of returning a copy of your string that may or may not be optimized, you can implement:



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

      

which returns a link, or perhaps

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

      

which does not "bind" your API to the string class (read about string_view

here , it was only officially added to the language in C ++ 17, but is available as std::experimental::string_view

with recent compilers).

+10


source


where is the vulnerability?

The answer is that function names can lie, but interfaces containing const references cannot.



Example:

#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;
}

      

+3


source


Strictly speaking, the method you write does not mutate class members. If you check it const

, the compiler will prevent it from mutating members entirely. But let's dig a little deeper here.

You usually write code once, but read / review it many times. Marking a method const

allows future readers to look at it and immediately know that the method cannot change the state of the class because the compiler will catch it. For example, if you accidentally write size_t empty() const { return size_ = 0; }

(where size_

is a member variable), the compiler will catch your typo. If you don't mark the const method, you will have a subtle error.

More importantly, methods const

can only call other methods const

. Consider if you have a method that takes the state of a class as input, does a bunch of work, and returns a result. If the methods getter

it uses to do its job are non-const, then a long complex method must also be non-const, which makes it more complex.

+2


source


The keyword function const

guarantees its use, do not change the object that was specified. Therefore, if you want to pass a print object to some function, or especially to operator<<

an outer class, you should only use the keyword method const

.

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

      

with error (compilation error)

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

      

+1


source


const

not about vulnerabilities! This usually does not provide security, safety, or anything else. Nothing prevents you from using a const-cast to remove it or using a keyword mutable

to change something.

const

helps to make your code cleaner. See Const-Correctness article for a good explanation of the concept.

  • When you look at something with const

    , you reasonably expect it to not change state. The declaration int Class:foo() const;

    tells me that foo()

    it won't change the state of the object, and it's ok to call it from anywhere.
  • const

    is infectious. The more you use it, the more you should use it. bool Class::bar();

    should be const

    if he needs to be called foo()

    .
  • The same applies in the opposite direction. If you pass const &

    : to functions void zoo(const Class &cls);

    , then you know you zoo()

    cannot change state cls

    .
0


source







All Articles