320 likes | 491 Views
C++ Certificate Program C++ Intermediate. Const. Const in C++. Differ greatly than what is defined in C Const objects, const methods, const declarations (references or pointers) Member constants may have class or instance scope. Constant Objects.
E N D
Const in C++ • Differ greatly than what is defined in C • Const objects, const methods, const declarations (references or pointers) • Member constants may have class or instance scope
Constant Objects • Const objects are not modifiable (once created) - cannot assign to or call non-const member functions • Review – initialization occurs at declaration (not assignment of a value after object creation) • Initialization is only place where values can be set (and all const objects must be initialized) int main() { int const y (0); Truck const bigTruck (“Mack”); y = 50; // illegal! }
Simple Const Objects • Simple const objects (variables) are of a built-in type, or classes without constructors (other restrictions apply as well) • Storage is not assigned by default • Initial values must be compile-time computable
Simple Const Variables and Storage • Ideally, the compiler would like to treat a constant like a define, except with type checking / safety • No storage would be allocated for the constant, which would result in a text replace where used int const myConst = 256; • Every use of myConst would be replaced with 256 (thus no storage allocated)
When Storage Must be Allocated • If the address of a const object is taken, storage must be allocated int const y = 256; int const& iref = y; // y now needs storage inn const* iptr = &y; // same here
Linkage • Unlike C, constants in C++ have internal linkeage, necessary to avoid allocating storage. float const pi; // legal in C, not in C++ • If external linkage forced (address taken), causes storage allocation float const pi = 3.1415….; // file1: definition extern float const pi; // file2: declaration, // causes storage allocation
Non-Simple Const Objects • Objects of class type can be also be const Employee const myBoss(“Bill”); • Object can’t be placed in read-only memory • Object typically can’t be “optimized away” • Constructor invoked when object created (as usual)
Constant Literal Folding • If possible, compilers will perform constant literal folding • Folding is the reduction of constants at runtime int x = 256*2+10; // will be reduced to 522 int const y = 1024; // no storage! int z = x + y - 100; // reduced to 546
Examples int const i (100); // note alternate initialization // syntax – parens instead of ‘=‘ int const j (i +10); // value from const expression int const * address (&j); // forces storage char buff[j+10]; // constant folding int func() { int const c = cin.get(); // not known at compile time int const c2 = c*2; }; // c goes out of scope
Class vs Instance Scope • Constant class member may have class scope (static - same value for all instances of the class) or instance scope (each object will have a potentially different value)
Class Scope Constants class X { public: static const int size = 256; }; • All instances of class X have a size member of the value 256. • Potentially, no storage will be allocated, and the constant is subject to constant folding • Definition must still be provided in implementation file // .cpp file int const X::size;
Instance Scope Constants • Instance scope constants must be initialized in initialization list (good style for all member data, of course), may not later be altered class X { public: X(int s) : size(s) {} private: int const size; }; • Storage is allocated, not subject to constant folding (since value is set when object is created)
Constant Pointers • Pointer may be const, the value pointed to may be const, or both int const* ptr1; // int is const, pointer is not const int* ptr2; // equivalent to the above! int* const ptr3; // const pointer to char int const* const ptr4; // const pointer to const int const int* const ptr5; // equivalent to above
References • Review: references are similar to automatically dereferenced pointers, where pointer itself is const, but object pointed to may or may not be const • Reference must be initialized at time of declaration, just like any other constant int y; int& iref = y; ++iref; // fine, the value referenced is not const int const& ciref = iref; ++ciref; // illegal, the value pointed at is // declared to be const
References • A reference itself may not be modified (syntax makes it difficult to do so, as well) • Because a reference by definition is const, it is redundant, and illegal, to explicitly declare them as such int const& iref const = y; // doh! illegal
Const Declarations • Pointers to const data, const references, const member functions provide interface usage and declaration intent - actual objects in use may or may not be const int x; int const* y =&x; int const& z = x; x = 10; // legal, x is not const *y = 10; // not legal z = 10; // not legal void f(int const & a); f(x); // x will not be modified during f lifetime
Const Declaration Style • Reading declarations: work from identifier outwards, const modifies what is “closest” • Traditional const declaration places const to the left of the type: const int x = 50; void f(const int&); • Some developers (still a minority) use “newer” const placement style (for consistency, since const always to right of what is const): int const x = 50; void f(int const&);
Const Conversions • Non-const object can always be converted to const; converse is not true void f(int const* x); void g(int* x); int* a; int const* b; f(a); // legal f(b); // legal g(a); // legal g(b); // error! Not legal
Const Part of Type • Const is part of the type and part of a function signature, crucial to overloading and general type distinctions • Many developers place pointer or reference symbol next to type to reinforce concept: int* x; // x’s type is int* int& y(a); // y’s type is int& int const& z (a); // z’s type is const int &
Instance Scope References • As with instance scope constants, instance scope references must be initialized in constructor initialization list class X { public: X(int val1, float val2) : myRef(val1), yourRef(val2) {} private: int& myRef; float const& yourRef; };
Pasing Function Args by Const Value • Sometimes used for documentation purposes int func(int const i); • Passing by value means copy of arg is made, const in this context is meaningless • However, can be meaningful inside the function implementation, which may not alter the value
Returning by Const Value • Return by const value may be useful (although it appears to be meaningless) because a temporary is created when dealing with user-defined types X func(); // return val may be used as an L-value X const func(); // cannot be used as an L-value
Example - Eckel, page 346 class X { int i; public: X(int ii = 0) : i(ii) {} void modify() {++i;} }; X f5() { return X(); } X const f6() { return X(); } void f7( X& x ) { x.modify(); } int main() { f5() = X(1); // legal – non-const return value f5().modify(); // legal (should it be allowed, though?) f7(f5()); // causes warning f6() = X(1); // compile–time error f6().modify(); // compile–time error f7(f6()); // compile–time error }
Temporaries • Compiler may create temp objects in an expression X(Y()); // instance of Y is temporary a + b + c; // two temporaries created • Created and destroyed by the compiler; user code cannot directly access them • Built-in type temporaries are const (class type temporaries are not const, however)
Const Member Functions • A const method (member function) cannot modify any self / “this” data (member data) class X { public : X(); // getState will not modify state of X int getState() const { return y; } private: int y; };
Passing and Returning Addresses • Pointer or reference (review: reference is an “alias” for an existing object) • Should be made const if at all possible to allow for use with const objects • Return const pointer or reference if wish to negate its use as a lval, or otherwise modify its value
Recommendation • For argument passing, choose reference first, const if possible – this eliminates questions that pointer flexibility may bring void f (int * p); // can p be null? Does it point to a single object // or an array of objects? • In general, use const whenever possible • Allows the compiler to make certain optimizations • Documents intent to other users
Const in Classes • Const class instances • Defined in the same manner as built-ins: int const y; X const x(1); • Const member functions • Const member data
Mutable • What does it mean for an object to be const? • Bitwise vs logical (memberwise) constness • Both compiler and linker enforce class constness • Const member functions may not change object members • Only const methods may be invoked on a const object • Const methods may not change state • In lieu of const_cast, the mutable keyword declares a member as modifiable in a const method
Mutable Example class X { public: X(int a = 0, b = 0) : x(a), y(b) {} int foo(); int bar() const { ++x; } // illegal int doh() const { ++y; } // OK, mutable private: int x; mutable int y; }; int main() { X const x(5,10); x.foo(); // illegal – can’t invoke non-const methods // on const object x.doh(); // OK }
Volatile • Tells compiler it cannot make assumptions about a variable in between uses • Multithreading, multitasking, or interrupt activity may change value of the variable outside of statement sequences • Compiler will often optimize with registers; volatile turns off this optimization, makes sure variable access uses memory address