// const: It allows you to communicate to both compilers and other programmers that a value should remain invariant // const/non-const pointer to const/non-const data char greeting[] = "Hello"; char * nonConstPointerNonConstData = greeting; const char * nonConstPointerConstData = greeting; char * const ConstPointerNonConstData = greeting; const char * const nonConstPointerConstData = greeting; // Both valid: function signature void f1(const int *ptr); void f2(int const *ptr); // STL iterators: pointer T* #include <vector> void DummyFunc() { std::vector<int> vec; const std::vector<int>::iterator pIter = vec.begin(); // like T * const *pIter = 10; // ok ++pIter; // error std::vector<int>::const_iterator pConstIter = vec.begin(); // like T * const *pConstIter = 10; // error ++pConstIter; // ok } // function const: apply on return value, parameter, entire member function class Rational {}; const Rational operator* (const Rational& lhs, const Rational& rhs); // If no return const, clients would be able to... void DummyFunc() { Rational a, b, c; (a*b) = c;// invoke operator= on the result of a*b if (a * b = c) {} // typo, but valid } /* const member function */ // 1. Identify which member functions may be invoked on const objects -> for program performance(pass objects by reference-to-const) // 2. Interface of a class easiser to understand // 3. Member functions differing only in their constness cand be overloaded class TextBlock { public: TextBlock(const std::string& str) : text(str) {} const char& operator[](std::size_t position) const { return text[position]; } // for const objects char& operator[](std::size_t position) { return text[position]; } // for non-const objects private: std::string text; }; #include <iostream> void DummyFunc() { TextBlock tb("Hello"); const TextBlock ctb("World"); std::cout << tb[0]; // call non-const TextBlock::operator[] std::cout << ctb[0]; // call const TextBlock::operator[] tb[0] = 'h'; // ok. If non-const TextBlock::operator[] return "char" -> error ctb[0] = 'w'; // error: attempt to make an assignment to a const char& } void print(const TextBlock& ctb) { // pass objects by reference-to-const std::cout << ctb[0]; // call const TextBlock::operator[] } /* Bitwise constness(physical constness) v.s. logical constness */ // 1. Bitwise const: a const member function iff not modify any of the object's non-static data members // 2. Easy to detect violation: just look for assignments to data members // 3. C++'s definition of constness // Member function that don't act very const pass the bitwise test: class CTextBlock { public: CTextBlock(char *strText) : pText(strText) {} char& operator[](std::size_t position) const { return pText[position]; } // inappropriate but bitwise const private: char *pText; }; void DummyFunc() { const CTextBlock cctb("Hello"); // const object char *pc = &cctb[0]; // call const operator[] *pc = 'h'; // oh no! cctb now "hello" } // Logical constness: a const member function might modify some of the bits in the object where it's invoked, but only in ways that clients cannot detect. class CTextBlock2 { public: std::size_t length() const; private: char *pText; mutable std::size_t textLength; mutable bool lengthIsValid; // mutable is a nice solution to bitwise-constness-is-not-what-I-had-in-mind problem }; std::size_t CTextBlock2::length() const { if (!lengthIsValid) { textLength = std::strlen(pText); // error if no mutable lengthIsValid = true; // error if no mutable } return textLength; } // Avoiding Duplication in const and non-const member functions class TextBlock { public: const char& operator[](std::size_t position) const { // heavy works: bounds checking, log access data, verify data integrity return text[position]; } char& operator[](std::size_t position) { // heavy works: bounds checking, log access data, verify data integrity return text[position]; } private: std::string text; }; // Solution 1: move all the same parts into a separate private member function (still duplicate function calls and return statement) // Solution 2: implement a non-const member function in terms of its const twin (the other way around has risk to change the logical state): class NoDupTextBlock { public: const char& operator[](std::size_t position) const { // heavy works: bounds checking, log access data, verify data integrity return text[position]; } char& operator[](std::size_t position) { return const_cast<char&>(static_cast<const NoDupTextBlock&>(*this)[position]); } private: std::string text; };
2018年10月22日 星期一
[Effective C++] Use const whenever possible
訂閱:
張貼留言 (Atom)
沒有留言:
張貼留言