수색…
통사론
- 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 :
기본 사항
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로 한정된 인스턴스를 모두 가져올 수 있고 멤버 함수가 호출 될 때 인스턴스의 this
가 const 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
올바른 코드가 그렇게하지 않는 것으로 가정하는 것이 일반적으로 안전합니다.