(e.g. #define C 1.5)
When should we use macros? (Not often)
When should we not use macros? (Most of the time)
#define MAX(a, b) f((a) > (b) ? (a) : (b))
MAX(++a, b), a could be incremented twice!template <typename T>
inline void max(const T& a, const T& b){
return f(a > b ? a : b);
}
(e.g. const int x = 3;)
When should we use consts?
When should we not use constants?
(e.g. enum { x = 3 };)
When should we use enums?
const’s.Const’s OftenHere are some ways to use them:
const Variableschar *p; // non const ptr, data
const char *p; // non-const pointer, const data
char * const p; // const pointer, non-const data
const char * const p; // const pointer, data
Rule of thumb:
const before the star modifies the type.const after the star modifies the pointer.typedef std::vector<int> v;
const v::iterator iter <= const pointer, non-const data
v::const_iterator iter <= non-const pointer, const data
Rule of thumb:
const iterator modifies the pointer.const_iterator modifies the data.const Rational operator*(const Rational& lhs, const Rational& rhs);
Means constant Rational returned from the * operator. Why does the result need to be constant? Because we could avoid:
(a*b) = c;
const Functionsconst functions are useful for the purpose of overloading:
class Bar{
const char& foo() const; //const function, returns const char&.
char& foo(); //function, returns char&
};
const functions inside classes cannot change the class members. By creating a class Foo f, we can call the non-const version of foo(), meanwhile const Foo f uses the const foo().
A way to allow for reduced code duplication between const and non-const equivalents, one can do the following:
class Bar{
private:
char c;
public:
const char& foo() const{
return c;
}
char& foo(){
/* Reuse code from foo() const: */
// removes const'ness
return const_cast<char&>(
// calls foo() const
static_cast<const Bar&>(*this).foo()
);
}
};
const vs. Logical constPhysical:
Logical(Recommended):
constness is only perceived by the user.mutable in front of variables, like mutable int x; to allow changeable class members even when const class instance.For non-member, built-in types, manually initialize:
int x = 0; // manual
char* cs = "hello world!"; // manual
For anything else, the onus is on the constructor.
/* Example of ASSIGNMENTS */
Foo::Foo(int x, char* s){
// Assigned DURING constructor
x_ = x;
s_ = s;
}
Here, it’s using the assignment operator =, to call the copy constructor. It calls this function: Foo& operator=(const Foo& rhs) { ... ; return *this; }
/* Example of INITIALIZATION */
Foo::Foo(int x, char* s) :
x_(x), s_(s)
{
// Initialization BEFORE constructor
}
Here, it’s directly passing in x and s as arguments of the copy constructors, which is this: Foo::Foo(const Foo& rhs) : ... { ... }
The crucial difference here is that assignments call the constructor of those objects first, and then we re-assign. This is a waste of resources compared to initialization with the arguments.
In addition, const variables cannot be assigned. They must be initialized, forcing them to have a value inside the initializer list.
static & Singleton DesignIf you have two files compiled in 2 different .so files like:
//f1.cpp
class Foo{
...
}
extern Foo foo; // allows any other cpp file to use it. Static non-local.
//f2.cpp
class Bar{
int x = foo.num();
...
}
Bar bar(); // uses foo.num()! Static non-local.
We have an issue here. We don’t know which order these objects are loaded.
Thus, we should implicitly define an order. We obviously want foo to exist when we call foo.num(), so we should define it in a static function:
//f1.cpp
Foo& foo(){ // Remind you of singleton by any chance?
static Foo foo;
return foo;
}
//f2.cpp
...
int x = foo().num();
This way, calling the function will be first, then we will be forced to create foo before bar is initialized statically.