Professional Documents
Culture Documents
Keywords
Keywords
Keywords
#define All header files should have #define guards to prevent multiple inclusion.
do looping construct
extern declares a variable or function and specifies that it has external linkage
or alternative to || operator
union a structure that assigns multiple variables to the same memory location
volatile warn the compiler about variables that can be modified unexpectedly
1. void main()
2. static int main()
3. int main(char** argv, int argc)
4. int main(int argc, char** argv)
5. int main()
6. inline int main(int argc, char* argv[])
7. int main(char* argv[], int argc)
8. int main(int argc, char* argv[])
9. int main(int argc, char* argv[], char* options[])
Ans: 5, 8, 9
auto
With C++ 11, we can use auto to declare types that can be deduced from
context so that we can iterate over complex data structures conveniently.
It is no longer a storage specifier which is used to put the auto variable to
a stack. Actually, this usage as a storage specifier has never been widely
used since its inception.
So, the following code
#include <iostream>
#include <list>
using namespace std;
int main ()
{
list<int> listA;
list<int> listB;
/*
Moves all elements from listB into *this.
The elements are inserted before the element pointed by iter.
The container listB becomes empty after the operation.
*/
listA.splice(iter, listB);
/*
Moves the elements in the range [iter, listA.end()) from
listA into *this.
The elements are inserted before the element pointed to by
listB.begin().
*/
listB.splice(listB.begin(), listA, iter, listA.end());
cout << "listA: " << listA << endl;
cout << "listB: " << listB << endl;
return 0;
}
Output:
listA: 1 2 3 4 5
listB: 100 200 300 400 500
listA: 1 2 3 100 200 300 400 500 4 5
listB:
listA: 1 2 3 100 200 300 400 500
listB: 4 5
const
const qualifier allows us to ask compiler to enforce a semantic constraint:
a particular object should not be modified. It also allows us to tell other
programmers that a value should remain invariant. The general form for
creating a constant is:
Note that we initialize a const in the declaration. So, the following line is
an error:
const correctness
What is const?
If we answer that question with "const is constant."
We may get 0 point for that answer.
If we answer with "const is read only."
Then, we may get some points.
Const correctness refers to use of the C++ const keyword to declare a
variable or method as immutable. It is a compile-time construct that can
be used to maintain the correctness of code that shouldn't modify certain
variables. We can define variables as const, to indicate that they should
not be modified, and we can also define methods as const, to mean that
they should not modify any member variables of the class. Using const
correctness is simply good programming practice. It can also provide
documentation on the intent of our methods, and hence make them
easier to use.
pointers
For pointers, we can specify whether the pointer itself is const, the data it
points to is const, both, or neither:
C++ doesn't allow the last line for simple reason: if we can assign the
address of year to p, then we can cheat and use p to modify the value
of year. That doesn't make sense because year is declared as const. C++
prohibits us from assigning the address of a const to a non-const pointer.
STL iterators
Since STL iterators are modeled on pointers, an iterator behaves mush
like a T* pointer. So, declaring an iterator as const is like declaring a
pointer const. If we want an iterator that points to something that can't be
altered (const T*), we want to use a const_iterator:
vector<int> v;
vector<int>::const_iterator itc = v.begin();
*itc = 2012; // error: *itc is cost
++itc; // ok, itc is not const
How about T*const iterator:
vector<int> v;
const vector<int>::iterator cit = v.begin;
*cit = 2012; // ok
++cit; // error: cit is const
arrays with const
Making a function to display the array is simple. We pass the name of the
array and the number of elements to the function. However, there are
some implications. We need to guarantee that the display doesn't change
the original array. In other words, we need to guard it from altering the
values of array. That kind of protection comes automatically with ordinary
parameters of the function because C++ passes them by value, and the
function plays with a copy. But functions that use an array play with the
original. To keep a function from accidentally modifying the contents of an
array, we can use the keyword const:
class Testing
{
public:
void foo() const {}
};
#include <iostream>
using namespace std;
class Testing
{
public:
Testing(int n):val(n){}
int getValue() const { return val; }
void setValue(int n) const { val = n; }
private:
int val;
};
int main()
{
Testing test1(10);
return 0;
}
There is another case which a member function appears against the const
declaration:
#include <iostream>
using namespace std;
class Testing
{
public:
Testing(int n):val(n){}
void foo1() const { foo2(); }
void foo2() {}
private:
int val;
};
int main()
{
Testing test1(10);
return 0;
}
#include <iostream>
using namespace std;
class Testing
{
public:
Testing(int n):val(n){}
int getVal() const {
cout << "getVal() const" << endl;
return val;
}
int getVal() {
cout << "getVal() non-const" << endl;
return val;
}
private:
int val;
};
int main()
{
const Testing ctest(10);
Testing test(20);
ctest.getVal();
test.getVal();
return 0;
}
Output is:
getVal() const
getVal() non-const
struct Foo
{
Foo() {}
void go()
{
std::cout << "Foo" << std::endl;
}
};
void go()
{
std::cout << "Bar" << std::endl;
}
};
int main(int argc, char** argv)
{
Bar b;
const Foo f = b;
return 0;
}
A Note On const.
#define
All header files should have #define guards to prevent multiple inclusion.
The format of the symbol name should
be <PROJECT>_<PATH>_<FILE>_H_.
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_
enum (Enumerations)
An enum is a very simple user-defined type, specifying its set of values as
symbolic constants.
#include <iostream>
enum Month {
Jan = 1, Feb, Mar, Apr, May, June,
Jul, Aug, Sep, Oct, Nov, Dec
};
int main()
{
using namespace std;
Month f = Feb;
Month j = Jul;
cout << "jj = " << jj << ", jjj = " << jjj << endl;
return 0;
}
Output is:
f = 2
jj = 7, jjj = 7
#define NumArrays 10
class ArrayObj
{
private:
int array[NumArrays];
};
int main()
{ ArrayObj a;
return 0;
}
class ArrayObj
{
private:
static const int NumArrays = 5;
int array[NumArrays];
};
class ArrayObj
{
private:
enum {NumArrays = 5};
int array[NumArrays];
};
typedef enum days {SUN, MON, TUE, WED, THURS, FRI, SAT} days;
days operator+(days d) {
return static_cast<days> ((static_cast<int>(d) + 1) % 7);
}
return os;
}
int main()
{
days d, a;
d = WED ;
a = +d;
cout << d << " " << a << endl;
d = SAT ;
a = +d;
cout << d << " " << a << endl;
return 0;
}
Output:
WED THURS
SAT SUN
explicit
Adding explicit is a good practice for any constructor that accepts a single
argument. It is used to prevent a specific constructor from being called
implicitly when constructing an object. For example, without
the explicit keyword, the following is valid C++ code:
Array a = 10;
Array::Array(int size) {}
checkArray(10,10);
class Complex
{
public:
Complex(double) // This defines double-to-complex
conversion
Complex(double, double)
};
...
Complex cmplx = 3.14 // OK: convert 3.14 to (3.14,0)
Complex cmplx = Complex(1.0, 2.5);
But this kind of conversion may cause unexpected and undesirable effects
as we see in the example below:
class Vector
{
int sz;
double *elem;
public:
Vector(int s): sz(s)
elem (new double[s]) {
for(int i=0; i< s; ++i) elem[i] = 0;
}
...
};
class Vector
{
...
Vector(int);
...
};
This weakens the type safety of our code because now the compiler will
not enforce the type of the first argument to be an
explicit Array/Vector object in the above examples. As a result, there is
the potential for the user to forget the correct order of arguments and
pass them in the wrong order. This is why we chould always use
the explicit keyword for any single argument constructors unless we
know that we want to support implicit conversion.
class Vector
{
...
explicit Vector(int);
...
};
inline
Though implementing a program as a set of functions is good from a
software engineering standpoint, function calls involve execution-time
overhead. So, C++ provides inline functions to help reduce function call
overhead, especially for small functions. Placing the qualifierinline before
a function's return type in the function definition tells the compiler to
generate a copy of the function's code in place to avoid a function call.
The trade-off is that multiple copies of the function code are inserted in
the program rather than there being a single copy of the function to which
control is passed each time the function is called. The compiler can ignore
the inline qualifier and typically does so for all but the smallest functions.
class MyClass
{
public:
void MyMethod() { }
};
There are exceptions to this rule to support templates and intentional use
of inline.
1. Pros
Inlining a function can generate more efficient object code, as long as the
inlined function is small. Feel free to inline accessors and mutators, and
other short, performance-critical functions.
2. Cons
Overuse of inlining can actually make programs slower. Depending on a
function's size, inlining it can cause the code size to increase or decrease.
Inlining a very small accessor function will usually decrease code size
while inlining a very large function can dramatically increase code size. On
modern processors smaller code usually runs faster due to better use of
the instruction cache.
3. Decision
A decent rule of thumb is to not inline a function if it is more than 10 lines
long. Beware of destructors, which are often longer than they appear
because of implicit member- and base-destructor calls!
Another useful rule of thumb: it's typically not cost effective to inline
functions with loops or switch statements (unless, in the common case,
the loop or switch statement is never executed).
It is important to know that functions are not always inlined even if they
are declared as such; for example, virtual and recursive functions are not
normally inlined. Usually recursive functions should not be inline. The
main reason for making a virtual function inline is to place its definition in
the class, either for convenience or to document its behavior, e.g., for
accessors and mutators.
mutable
To allow a class data member to be modified even though it is the data
member of a constobject, we can declare the data member as mutable. A
mutable member is a member that is never const, even when it is the
data member of a const object. A mutable member can always be
updated, even in a const member function.
struct account
{
char name[50];
mutable int id;
};
#include <iostream>
#include <cstring>
class MyText
{
public:
std::size_t getLength() const;
private:
char * ptrText;
std::size_t txtLen;
};
class MyText
{
public:
std::size_t getLength() const;
private:
char * ptrText;
mutable std::size_t txtLen;
};
How can we make the code work by making changes only inside class C
#include <iostream>
class C
{
private:
int num;
public:
C(int a) : num(a) {}
int get_val() const;
};
int main()
{
C obj(29);
std::cout << obj.get_val() << std::endl;
}
Ans: mutable int num;
namespaces
As programming projects grow large, the potential for name conflicts
increases. The language mechanism for organizing classes, functions,
data, and types into an identifiable and named part of a program without
defining a type is a namespace. The C++ standard provides namespace
which allows us to have greater control over the scope of names.
namespace Stock {
double penny;
void order();
int amount;
struct Option { ... };
}
namesapce Market {
double dollar;
void order();
int amount;
struct Purchase { ... };
}
The names in any one namespace don't conflict with names in another
namespace. So, the order in Stock is not confused with
the order in Market
namespace Stock {
string getCompanyName();
}
Stock::amount = 200;
Market::Purchase p;
Market::order();
As a quick summary:
If we want to reference "j" in "main()", how do we do that?
namespace
{
int j;
}
int main()
{
// ??
return 0;
}
Answer:
int i = ::j;
So, it looks like this when we have j both in global and local scope:
#include <iostream>
namespace
{
int j;
}
int main()
{
::j = 911;
int j = 800;
int n1 = ::j;
int n2 = j;
return 0;
}
Output:
n1 = 911
n2 = 800
register
The register keyword is a hint to the compiler that we want it to provide
fast access to the variable, perhaps by using a CPU register instead of the
stack to handle a particular variable. The CPU can access a value in one of
its registers more quickly than it can access memory in the stack. Some
compilers may ignore the hint and use register allocation algorithms to
figure out the best candidates to be placed within the available machine
registers. Because the compiler is aware of the machine architecture on
which the program is run, it is often able to make a more informed
decision when selecting the content of machine registers.
Array indexes and pointers occurring within a loop are good candidates
for register objects.
struct
The keyword struct introduces a structure declaration, which is a list of
declarations enclosed in braces.
struct structure_name {
type1 member1;
type2 member2;
} object_name;
myData.idata = 10;
myData.fdata = 3.14;
Once used like above, the data can represent for the declaration {...}, and
it can be used later in definition of instances of the structure. For example,
it can be used like this:
#include <stdio.h>
struct data
{
int idata;
float fdata;
} myData[] = {
{10, 3.14},
{20, 0.314},
{30, 0.0314}
};
int main()
{
printf("%d,%f\n", myData[0].idata,myData[0].fdata);
printf("%d,%f\n", myData[1].idata,myData[1].fdata);
printf("%d,%f\n", myData[2].idata,myData[2].fdata);
printf("%d\n", sizeof data); // 8
printf("%d\n", sizeof myData); // 24
printf("%d\n", sizeof *myData); // 8
return 0;
}
or
The 2nd one is better because it does not need to be changed if the type
changes. For more on the size of struct, see Size of struct
The struct is very useful when we build linked list:
struct list {
int data;
struct list *next;
};
The recursive declaration looks illegal because it's referring itself. But it's
not. It's not containing an instance of itself, but
typedef struct t
{
int i;
float f;
t* ptr;
} T;
How can we initialize the array of that struct?
Here is the answer:
T myStruct[10] = {{0}};
switch
The example below is a sort of quiz for switch related
to break and continue. We should be able to guess the output from the
code:
#include <iostream>
using namespace std;
int main()
{
for (int i = 0; i < 5; ++i)
{
switch (i)
{
case 0 : cout << "0";
case 1 : cout << "1"; continue;
case 2 : cout << "2"; break;
case 3 : cout << "3"; break;
default : cout << "d"; break;
}
cout << "*";
}
return 0;
}
0112*3*d*
typedef
We can define a new name for an existing type. We use typedef to create
an alias:
typedef typeName aliasName;
As an another example:
struct node
{
int data;
node *next;
};
typedef node node_t;
or
using
C++ provides two mechanisms to qualify names:
But we should not use the using keyword in the global scope of our public
headers because it would against the purpose of using namespaces in the
first place. If we want to reference symbols in another namespace in our
header, we should use fully qualified name, i.e., std::string.
volatile
The volatile keyword indicates that the value in a memory location can be
altered in ways unknown to the compiler or have other unknown side
effects (e.g. modification via a signal interrupt, hardware register,
or memory mapped I/O) even though nothing in the program code
modifies the contents. In other words, volatile informs the compiler that
the value of the variable can change from the outside, without any update
done by the code.
More on volatile:
References on volatile: