Print service provided by iDogiCat: http://www.idogicat.com/
home logo





Home > IT > Programming > C++ Programming > iDog's Interview C++ > iDog's Interview C++ Part 1

iDog's Interview C++ Part 1

Basic, Const, Reference, Pointer, Array

About C++

C++ fully supports OOP, including the four pillars of OOD:

  • encapsulation
  • data hiding
  • inheritance
  • polymorphism

Recursion

Recursion is a tricky and powerful method. It makes the code very clear and short, but it may eat up a lot of memory.

Calc Fibonacci number:

Index1234567...
Fib num11235813...


// recursion version
int fib_r(int i)
{
    assert(i > 0);
    if(i < 3)  return 1;
    else  return fib_r(i - 1) + fib_r(i - 2);
}

// non-recursion version
int fib(int i)
{
    assert(i > 0);
    if(i < 3)  return 1;

    int nPrev = 1;
    int nCurr = 1;
    for(int j = 3; j <= i; j++) {
        int nTemp = nCurr;
        nCurr += nPrev;
        nPrev = nTemp;
    }
    return nCurr;
}

If i is very big, using recursion version can be very slow: there are a lot of cost of calling functions.

Memory

Memory areas

  • Global name space: holds global variables
  • Code space: holds code
  • registers: holds internal housekeeping functions (such as keeping track of stack top and instruction pointer)
  • stack: holds local variables, function parameters, function return value
  • heap (or 'free store'): a massive section of memory for dynamically allocated variables.

Memory leak: dynamically allocated a block of memory without calling delete to delete it.

Stray pointers: Stray pointers are often called wild pointers or dangling pointers, are pointers used after they are deleted.


int* pn = new int;
*pn = 3;
delete pn;
*pn = 4;  // stray pointer

Size of a class: The size of a class is the sum of all it's member variables. Memory taken by its member functions is not calculated. But if there are any virtual functions, some extra memory taken is also calculated in the size of the class.

size_of_a_class = sum(size_of_member_var) + size_of_v_table

Size of a reference is the size of the target it refers to.

Note that due to memory alignment (when allocating mem, a unit is 4 bytes in 32-bit env), size of a class or struct may be greater than sum of all their member variables:


struct S {
    long l;
    char c;
} s;

class C {
    long l;
    char c1;
    char c2;
} c;

// in 32-digit env, sizeof(s) is 8; sizeof(c) is 8
// class C1 {char c1; char c2; char c3; char c4;};     <-- size = 4
// class C2 {char c1; char c2; char c3; char c4; char c5;};     <-- size = 8

Example sizes of basic types:

Windows XP or Linux on a 32-bit PC:

typesizeNote
bool1always 1
char1always 1
short2half of int-size
int432-bit, so 4
long4twice of int-size, or same size
float4half of double-size
double8 
char*4pointers: int-size

Note: '32-bit platform' means the size of int is 32-bit, that is 4 bytes (1 byte = 8 bits)

Comparison to Java data types:

sizeC++ on 32-bit OSJavaNote
1bool, charboolean, byte 
2wchar_t, shortchar, shortJava chars are in Unicode
4int, long, float, pointersint, float 
8doublelong, doubleJava 'long' is really longer than int

Constant

const and pointers:


// const data, non-const pointer
const int* pPointerToConstValue = &nVariable;
int const * pPointerToConstValue1 = &nVariable; // another form

*pPointerToConstValue = 2; // wrong
pPointerToConstValue = &nAnotherVariable; // OK

// const pointer, non-const data
int* const pConstPointer = &nVariable;

pConstPointer = &nAnotherVariable; // wrong
*pConstPointer = 2; // OK

// const pointer and data
const int* const pConstPointerToConstValue = &nVariable;

pConstPointerToConstValue = &nAnotherVariable; // wrong
*pConstPointerToConstValue = 2; // wrong

BTW, following statement is common, but not recommended:


char* p = "Hello!";
because "Hello!" is of type const char[]. We'd better write it in following way:

const char* p = "Hello!";

Note: following code generates a runtime error:


char* p = "Hello";
p[4] = '!';  // this is not allowed! 
             // but this compiles since p is a 'char*'.
cout << p << endl;
Note: if we change char* p to char p[], it works, because the "Hello" is simply a const string used to init the array.

A pointer pointing to a const instance of a class can only call const functions of the class:


const MyClass* pMyClass = new MyClass;
pMyClass->myConstFunc();
pMyClass->myNonConstFunc(); // wrong

Constant member functions: A member function can be declared const if it does not modify any member of the class. But it can modify the 'mutable' member variables (NEW STANDARD).

mutable & volatile

mutable: Meaning of 'mutable' in English is 'liable to change'. It's used to define a member variable in a class that can be modified by a const member function.

volatile: Meaning of 'volatile' in English is 'unstable'. It's used to turn off compiler optimization on it, so the program accesses on itself all the time. This is mainly used in multithreaded programs when some variables' values are not certain at a given time because many threads may change it. BTW, making a variable volatile decreases the speed to access it.

Reference

A reference is an alias of the variable it refers to (the 'target').

A reference can never be reassigned to refer to another target. So it's always constant.


int n1 = 1;
int n2 = 2;

const int& rn1 = n1;
rn1 = n2; // wrong, since it's const

int& const rn2 = n1; // the const is useless

If you ask a reference for its address, it returns the address of its target; if you ask a reference for its size, it also returns the size of its target.

Pointers can be null; references cannot be null.

When to use pointers and when to use references

  • references should be prefered since it's cleaner and easier to use.
  • references cannot be resigned, so if you need to point to one object and then to another, you must use a pointer
  • references cannot be null, so if the object can be null, you must use a pointer
  • Warning: don't return a reference to an object that is out of scope!
  • Don't return a reference to an object on the heap, because you cannot delete it (since references cannot be null)!

How to return an object in a function

  1. create the object on the stack, and return the object by value;
  2. allocate the object on the heap, and return a pointer to it. Note that this is not a good practice: a block of memory should be allocated and freed in the same scope. Otherwise it would be error prone.
  3. first declare the object before calling this function, and pass a reference to the object to this function.


// Method 1

MyObj myfunc()
{
    MyObj myObj();
    return myObj;
}

// Method 2

MyObj* myfunc()
{
    MyObj* pMyObj = new MyObj();
    return pMyObj;
}

// you need to remember to delete pMyObj at some place


// Method 3

MyObj& myfunc(MyObj& rMyObj)
{
    // modify the value of rMyObj if needed
    rMyObj.changeValue();
    return rMyObj;
}

Pointer

A pointer is a variable which value is a memory address.

DO: Always initialize your pointer! Initialize it with an address or NULL.

Pointers to functions:

Format:

// to ordinary function
return_type (* function_pointer_name) (parameter_list);

// to member function of class
return_type (class_name:: * function_pointer_name) (parameter_list) [const];

Examples:


int (*pFunc)(int, char);
int myFunc(int, char);
pFunc = myFunc; // or: pFunc = &myFunc;
(*pFunc)(1, 'a');
pFunc(1, 'a');  // simplified format, recomended.

Pointer to member functions of a class:


class MyClass
{
public:
    void f1(int);
    void f2(int);
};

// declare a function pointer to member functions of class MyClass,
//   and init it with NULL
void (MyClass::*pf)(int) = NULL;

pf = &MyClass::f1;  // must use '&' in this case

MyClass a;
MyClass* pa = &a;
(a.*pf)(1);
(pa->*pf)(2);  // use .* or ->* operators

Complicated pointers:

Those const ones are omitted here.


int (*pFunc)();
int* funcReturnsIntPointer();
int* (* pFuncArray[10]) (int);

int** (**p[])() = {a, b, c};

// typedef can be used to improve the readability
typedef int**(*func_ptr)(); // pointer to functions like int** f()
func_ptr* p[] = {a, b, c};

Dereference & Increment/Decrement

Precedence of dereference operator (*) is lower than those of increment/decrement operators. The C gurus often write code in following ways:


char c;
c = *psz++; // c = *(psz++); or: c = *psz; psz++;
c = *++psz; // c = *(++psz); or: psz++; c = *psz;

Array

Array name is a const pointer to its first element.

A tip: let the compiler set the size of initialized array:


MyType myArray[] = {a, b, c, d, e, f, g};
int nSize = sizeof(MyArray) / sizeof(MyArray[0]);
In this way, when you want to add or delete some elements assigned to the array, you don't need to change your code other than the data (or, you don't need to count them by yourself).