수색…


통사론

  • class ClassOne {public : bool non_modifying_member_function () const {/ * ... * /}};
  • int ClassTwo :: non_modifying_member_function () const {/ * ... * /}
  • void ClassTwo :: modifying_member_function () {/ * ... * /}
  • char non_param_modding_func (const ClassOne & one, const ClassTwo * two) {/ * ... * /}
  • float parameter_modifying_function (ClassTwo & one, ClassOne * two) {/ * ... * /}
  • 짧은 ClassThree :: non_modding_non_param_modding_f (const ClassOne &) const {/ * ... * /}

비고

const 정확성은 프로그래머가 부주의하게 코드를 수정할 수있는 기능을 신속하게 판별 할 수있게 해주므로 매우 유용한 문제 해결 도구입니다. 또한 Const Correct Function Parameters 표시된 것과 같은 의도하지 않은 오류가 제대로 컴파일되지 않고 눈에 띄지 않게합니다.

const 정확성을 위해 클래스를 디자인하는 것이 훨씬 쉽습니다. 나중에 기존 클래스에 const 정확성을 추가하는 것보다 쉽습니다. 가능하면 할 수있는 클래스 설계 const 이되도록 올바른 const , 올바른을 자신과 다른 사람들에게 그것을 수정 이후의 번거 로움을 저장합니다.

이것은 const 정확성과 동일한 규칙을 사용하여 필요할 경우 volatile 정확도에도 적용 할 수 있지만 훨씬 덜 자주 사용됩니다.

Refrences :

ISO_CPP

나를 const 정확성에 팔아라.

C ++ 튜토리얼

기본 사항

const 정확성 은 인스턴스를 수정 해야하는 코드 (예 : 쓰기 액세스 권한) 만 인스턴스를 수정하고 인스턴스를 수정할 필요 가없는 코드는 수행 할 수 없도록 코드를 설계하는 관행입니다 그래서 (즉, 읽기 액세스 만 가능). 이렇게하면 실수로 인스턴스가 수정되는 것을 방지하고 코드의 오류를 줄이며 코드가 인스턴스의 상태를 변경할지 여부를 문서화합니다. 인스턴스가 수정 될 필요가 없을 때마다 인스턴스를 const 로 처리하거나, 초기화 후에도 변경하지 않아도되는 경우 const 정의 된 인스턴스를 기능 손실없이 처리 할 수 ​​있습니다.

이는 멤버 함수를 제공하여 수행됩니다 const CV-예선 , 포인터 / 참조 매개 변수 만들어서 const 가 쓰기 액세스를 필요로하는 경우를 제외하고.

class ConstCorrectClass {
    int x;

  public:
    int getX() const { return x; } // Function is const: Doesn't modify instance.
    void setX(int i) { x = i; }    // Not const: Modifies instance.
};

// Parameter is const: Doesn't modify parameter.
int const_correct_reader(const ConstCorrectClass& c) {
    return c.getX();
}

// Parameter isn't const: Modifies parameter.
void const_correct_writer(ConstCorrectClass& c) {
    c.setX(42);
}

const ConstCorrectClass invariant; // Instance is const: Can't be modified.
ConstCorrectClass         variant; // Instance isn't const: Can be modified.

// ...

const_correct_reader(invariant); // Good.   Calling non-modifying function on const instance.
const_correct_reader(variant);   // Good.   Calling non-modifying function on modifiable instance.

const_correct_writer(variant);   // Good.   Calling modifying function on modifiable instance.
const_correct_writer(invariant); // Error.  Calling modifying function on const instance.

const 정확성의 특성으로 인해 클래스의 멤버 함수에서 시작하여 바깥쪽으로 작동합니다. 당신은 비를 호출하려고하면 const A로부터 멤버 함수 const 인스턴스를 비에서, 또는 const 인스턴스로 치료를 받고 const , 컴파일러는 당신이 이력서 - 예선 손실에 대한 오류를 줄 것이다.

Const 올바른 클래스 디자인

const -correct 클래스에서 논리적 상태를 변경하지 않는 모든 멤버 함수는 this cv- 수식어가 const 인데, const 인스턴스에서도 자유롭게 수정할 수있는 임의의 mutable 필드는 제외하고 개체를 수정하지 않음을 나타냅니다 ); const cv-qualified 함수가 참조를 반환하면 해당 참조도 const 이어야합니다. const T*T* 또는 const T* 에 바인딩 할 수 있기 때문에이를 통해 상수 및 비 Cv 검증 인스턴스에서 호출 할 수 있습니다. 이것은 함수가 기능을 잃지 않고 참조로 전달 된 매개 변수를 수정할 필요가 없을 때 const 로 선언하도록합니다.

또한 const 올바른 클래스에서 참조로 전달 된 함수 매개 변수는 모두 Const Correct Function Parameters 에서 설명한대로 올바른 const 되므로 함수가 명시 적으로 수정 해야하는 경우에만 수정할 수 있습니다.

먼저, 살펴 보자 this 이력서 - 예선 :

// Assume class Field, with member function "void insert_value(int);".

class ConstIncorrect {
    Field fld;

  public:
    ConstIncorrect(Field& f); // Modifies.

    Field& getField();        // Might modify.  Also exposes member as non-const reference,
                              //  allowing indirect modification.
    void setField(Field& f);  // Modifies.

    void doSomething(int i);  // Might modify.
    void doNothing();         // Might modify.
};

ConstIncorrect::ConstIncorrect(Field& f) : fld(f) {} // Modifies.
Field& ConstIncorrect::getField() { return fld; }    // Doesn't modify.
void ConstIncorrect::setField(Field& f) { fld = f; } // Modifies.
void ConstIncorrect::doSomething(int i) {            // Modifies.
    fld.insert_value(i);
}
void ConstIncorrect::doNothing() {}                  // Doesn't modify.


class ConstCorrectCVQ {
    Field fld;

  public:
    ConstCorrectCVQ(Field& f);     // Modifies.

    const Field& getField() const; // Doesn't modify.  Exposes member as const reference,
                                   //  preventing indirect modification.
    void setField(Field& f);       // Modifies.

    void doSomething(int i);       // Modifies.
    void doNothing() const;        // Doesn't modify.
};

ConstCorrectCVQ::ConstCorrectCVQ(Field& f) : fld(f) {}
Field& ConstCorrectCVQ::getField() const { return fld; }
void ConstCorrectCVQ::setField(Field& f) { fld = f; }
void ConstCorrectCVQ::doSomething(int i) {
    fld.insert_value(i);
}
void ConstCorrectCVQ::doNothing() const  {}

// This won't work.
// No member functions can be called on const ConstIncorrect instances.
void const_correct_func(const ConstIncorrect& c) {
    Field f = c.getField();
    c.do_nothing();
}

// But this will.
// getField() and doNothing() can be called on const ConstCorrectCVQ instances.
void const_correct_func(const ConstCorrectCVQ& c) {
    Field f = c.getField();
    c.do_nothing();
}

그런 다음이를 Const Correct Function Parameters 와 결합하면 클래스가 완전히 const correct가됩니다.

class ConstCorrect {
    Field fld;

  public:
    ConstCorrect(const Field& f);  // Modifies instance.  Doesn't modify parameter.

    const Field& getField() const; // Doesn't modify.  Exposes member as const reference,
                                   //  preventing indirect modification.
    void setField(const Field& f); // Modifies instance.  Doesn't modify parameter.

    void doSomething(int i);       // Modifies.  Doesn't modify parameter (passed by value).
    void doNothing() const;        // Doesn't modify.
};

ConstCorrect::ConstCorrect(const Field& f) : fld(f) {}
Field& ConstCorrect::getField() const { return fld; }
void ConstCorrect::setField(const Field& f) { fld = f; }
void ConstCorrect::doSomething(int i) {
    fld.insert_value(i);
}
void ConstCorrect::doNothing() const {}

이는 인스턴스에 const 가있는 경우 하나의 동작이 필요하고 const 가 아닌 경우 다른 동작이 필요한 경우에는 const 기준으로 오버로드와 결합 할 수도 있습니다. 이것에 대한 일반적인 사용은 컨테이너 자체가 비 const 경우 수정 만 허용하는 접근자를 제공하는 제약 자입니다.

class ConstCorrectContainer {
    int arr[5];

  public:
    // Subscript operator provides read access if instance is const, or read/write access
    // otherwise.    
          int& operator[](size_t index)       { return arr[index]; }
    const int& operator[](size_t index) const { return arr[index]; }

    // ...
};

이는 일반적으로 대부분의 컨테이너가 취할 과부하를 제공하여, 표준 라이브러리에 사용되는 const 고려 다움을.

Const 올바른 함수 매개 변수

const -correct 함수에서 함수가 직접 또는 간접적으로 수정하지 않는 한 참조로 전달 된 모든 매개 변수는 const 로 표시되므로 프로그래머가 의도하지 않은 무언가를 실수로 변경하는 것을 방지 할 수 있습니다. 이렇게하면 함수가 const 및 non-cv로 한정된 인스턴스를 모두 가져올 수 있고 멤버 함수가 호출 될 때 인스턴스의 thisconst T* 유형이됩니다. 여기서 T 는 클래스 유형입니다.

struct Example {
    void func()       { std::cout << 3 << std::endl; }
    void func() const { std::cout << 5 << std::endl; }
};

void const_incorrect_function(Example& one, Example* two) {
    one.func();
    two->func();
}

void const_correct_function(const Example& one, const Example* two) {
    one.func();
    two->func();
}

int main() {
    Example a, b;
    const_incorrect_function(a, &b);
    const_correct_function(a, &b);
}

// Output:
3
3
5
5

이것의 효과는 덜 즉시 표시보다 있지만 const 올바른 클래스 디자인 (점에서 const 순찰 중 기능과 const 부정확 한 클래스 컴파일 오류가 발생합니다, 반면 const 순찰 중 클래스와 const 부정확 한 기능이 제대로 컴파일), const 올바른 함수는 오류를 많이 잡을 const 등 아래와 같이 통해 미끄러 할 것입니다 잘못된 기능입니다. 그러나 const incorrect 함수 const 인스턴스가 아닌 const 인스턴스를 전달할 때 컴파일 오류를 발생시킵니다.]

// Read value from vector, then compute & return a value.
// Caches return values for speed.
template<typename T>
const T& bad_func(std::vector<T>& v, Helper<T>& h) {
    // Cache values, for future use.
    // Once a return value has been calculated, it's cached & its index is registered.
    static std::vector<T> vals = {};

    int v_ind = h.get_index();               // Current working index for v.
    int vals_ind = h.get_cache_index(v_ind); // Will be -1 if cache index isn't registered.

    if (vals.size() && (vals_ind != -1) && (vals_ind < vals.size()) && !(h.needs_recalc())) {
        return vals[h.get_cache_index(v_ind)];
    }

    T temp = v[v_ind];

    temp -= h.poll_device();
    temp *= h.obtain_random();
    temp += h.do_tedious_calculation(temp, v[h.get_last_handled_index()]);

    // We're feeling tired all of a sudden, and this happens.
    if (vals_ind != -1) {
        vals[vals_ind] = temp;
    } else {
        v.push_back(temp);  // Oops.  Should've been accessing vals.
        vals_ind = vals.size() - 1;
        h.register_index(v_ind, vals_ind);
    }

    return vals[vals_ind];
}

// Const correct version.  Is identical to above version, so most of it shall be skipped.
template<typename T>
const T& good_func(const std::vector<T>& v, Helper<T>& h) {
    // ...

    // We're feeling tired all of a sudden, and this happens.
    if (vals_ind != -1) {
        vals[vals_ind] = temp;
    } else {
        v.push_back(temp);  // Error: discards qualifiers.
        vals_ind = vals.size() - 1;
        h.register_index(v_ind, vals_ind);
    }

    return vals[vals_ind];
}

문서로서의 Const 정확성

const 정확성에 대해 더 유용한 것들 중 하나는 코드를 문서화하는 방법으로 사용되어 프로그래머와 다른 사용자에게 확실한 보장을 제공한다는 것입니다. 이 보증은 인해 컴파일러에 의해 적용됩니다 const 의 부족으로, 다움 const 를 제공하지 않는 코드를 나타내는 차례로 다움.

const CV- 한정 멤버 함수 :

  • const 멤버 함수는 인스턴스를 읽으려는 의도가 있다고 가정 할 수 있습니다.
    • 호출 된 인스턴스의 논리적 상태는 수정하지 않아야합니다. 그러므로 그들은 mutable 변수를 제외하고 호출 된 인스턴스의 멤버 변수를 수정해서는 안됩니다.
    • mutable 변수를 제외하고는 인스턴스의 멤버 변수를 수정하는 다른 함수를 호출하지 않아야합니다.
  • 반대로, const 가 아닌 멤버 함수는 인스턴스를 수정하려는 의도가 있다고 가정 할 수 있습니다.
    • 논리 상태를 변경하거나 수정하지 않을 수 있습니다.
    • 논리 상태를 수정하는 다른 함수를 호출 할 수도 있고 호출하지 않을 수도 있습니다.

이것은 함수의 정의를 보지 않아도 주어진 멤버 함수가 호출 된 후에 객체의 상태에 대한 가정을하는 데 사용할 수 있습니다.

// ConstMemberFunctions.h

class ConstMemberFunctions {
    int val;
    mutable int cache;
    mutable bool state_changed;

  public:
    // Constructor clearly changes logical state.  No assumptions necessary.
    ConstMemberFunctions(int v = 0);

    // We can assume this function doesn't change logical state, and doesn't call
    //  set_val().  It may or may not call squared_calc() or bad_func().
    int calc() const;

    // We can assume this function doesn't change logical state, and doesn't call
    //  set_val().  It may or may not call calc() or bad_func().
    int squared_calc() const;

    // We can assume this function doesn't change logical state, and doesn't call
    //  set_val().  It may or may not call calc() or squared_calc().
    void bad_func() const;

    // We can assume this function changes logical state, and may or may not call
    //  calc(), squared_calc(), or bad_func().
    void set_val(int v);
};

const 규칙으로 인해 이러한 가정은 사실 컴파일러에 의해 강제 적용됩니다.

// ConstMemberFunctions.cpp

ConstMemberFunctions::ConstMemberFunctions(int v /* = 0*/)
  : cache(0), val(v), state_changed(true) {}

// Our assumption was correct.
int ConstMemberFunctions::calc() const {
    if (state_changed) {
        cache = 3 * val;
        state_changed = false;
    }

    return cache;
}

// Our assumption was correct.
int ConstMemberFunctions::squared_calc() const {
    return calc() * calc();
}

// Our assumption was incorrect.
// Function fails to compile, due to `this` losing qualifiers.
void ConstMemberFunctions::bad_func() const {
    set_val(863);
}

// Our assumption was correct.
void ConstMemberFunctions::set_val(int v) {
    if (v != val) {
        val = v;
        state_changed = true;
    }
}

const 함수 매개 변수 :

  • const 하나 이상의 매개 변수가있는 함수는 해당 매개 변수를 읽으려는 의도가 있다고 가정 할 수 있습니다.
    • 이러한 매개 변수를 수정하지 않거나 매개 변수를 수정하는 멤버 함수를 호출해야합니다.
    • 이 매개 변수를 수정하거나 수정하는 멤버 함수를 호출하는 다른 함수에 매개 변수를 전달하지 않아야합니다.
  • 반대로, const 가 아닌 하나 이상의 매개 변수가있는 함수는 해당 매개 변수를 수정하려는 의도가 있다고 가정 할 수 있습니다.
    • 이러한 매개 변수를 수정하거나 수정하지 않을 수도 있고 수정할 수있는 멤버 함수를 호출 할 수도 있습니다.
    • 해당 매개 변수를 수정하거나 수정하는 멤버 함수를 호출하는 다른 함수에 매개 변수를 전달할 수도 있고 전달하지 않을 수도 있습니다.

이것은 함수의 정의를 보지 않아도 주어진 함수에 전달 된 후에 매개 변수의 상태에 대한 가정을하는 데 사용될 수 있습니다.

// function_parameter.h

// We can assume that c isn't modified (and c.set_val() isn't called), and isn't passed
//  to non_qualified_function_parameter().  If passed to one_const_one_not(), it is the first
//  parameter.
void const_function_parameter(const ConstMemberFunctions& c);

// We can assume that c is modified and/or c.set_val() is called, and may or may not be passed
//  to any of these functions.  If passed to one_const_one_not, it may be either parameter.
void non_qualified_function_parameter(ConstMemberFunctions& c);

// We can assume that:
  // l is not modified, and l.set_val() won't be called.
  // l may or may not be passed to const_function_parameter().
  // r is modified, and/or r.set_val() may be called.
  // r may or may not be passed to either of the preceding functions.
void one_const_one_not(const ConstMemberFunctions& l, ConstMemberFunctions& r);

// We can assume that c isn't modified (and c.set_val() isn't called), and isn't passed
//  to non_qualified_function_parameter().  If passed to one_const_one_not(), it is the first
//  parameter.
void bad_parameter(const ConstMemberFunctions& c);

const 규칙으로 인해 이러한 가정은 사실 컴파일러에 의해 강제 적용됩니다.

// function_parameter.cpp

// Our assumption was correct.
void const_function_parameter(const ConstMemberFunctions& c) {
    std::cout << "With the current value, the output is: " << c.calc() << '\n'
              << "If squared, it's: " << c.squared_calc()
              << std::endl;
}

// Our assumption was correct.
void non_qualified_function_parameter(ConstMemberFunctions& c) {
    c.set_val(42);
    std::cout << "For the value 42, the output is: " << c.calc() << '\n'
              << "If squared, it's: " << c.squared_calc()
              << std::endl;
}

// Our assumption was correct, in the ugliest possible way.
// Note that const correctness doesn't prevent encapsulation from intentionally being broken,
//  it merely prevents code from having write access when it doesn't need it.
void one_const_one_not(const ConstMemberFunctions& l, ConstMemberFunctions& r) {
    // Let's just punch access modifiers and common sense in the face here.
    struct Machiavelli {
        int val;
        int unimportant;
        bool state_changed;
    };
    reinterpret_cast<Machiavelli&>(r).val = l.calc();
    reinterpret_cast<Machiavelli&>(r).state_changed = true;

    const_function_parameter(l);
    const_function_parameter(r);
}

// Our assumption was incorrect.
// Function fails to compile, due to `this` losing qualifiers in c.set_val().
void bad_parameter(const ConstMemberFunctions& c) {
    c.set_val(18);
}

const 정확성피할 있지만 확장을 통해 이러한 보증을 깨뜨리는 것은 프로그래머가 의도적으로 수행해야합니다 (위의 Machiavelli 캡슐화를 끊는 것과 동일). 정의되지 않은 동작이 발생할 수 있습니다.

class DealBreaker : public ConstMemberFunctions {
  public:
    DealBreaker(int v = 0);

    // A foreboding name, but it's const...
    void no_guarantees() const;
}

DealBreaker::DealBreaker(int v /* = 0 */) : ConstMemberFunctions(v) {}

// Our assumption was incorrect.
// const_cast removes const-ness, making the compiler think we know what we're doing.
void DealBreaker::no_guarantees() const {
    const_cast<DealBreaker*>(this)->set_val(823);
}

// ...

const DealBreaker d(50);
d.no_guarantees(); // Undefined behaviour: d really IS const, it may or may not be modified.

그러나 프로그래머가 컴파일러 에게 항상 const 를 무시하고 컴파일러간에 일관성이 없다는 것을 컴파일러 에게 알리 도록 요구하기 때문에 달리 명시하지 않는 한 const 올바른 코드가 그렇게하지 않는 것으로 가정하는 것이 일반적으로 안전합니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow