C/C++ FAQ

C

Size of integer Types

C stacndard only defines min size for primitive integer data types.

2 <= sizeof(short) <= sizeof(int) <= sizeof(long) >= 4

  • long / pointer / size_t (unsigned type) are platform-dependency (32-bits/64-bits)

padding, alignment, union

reference: Data structure alignment

The CPU performs reads and writes to memory most efficiently when the data is naturally aligned. To ensure natural alignment, it may be necessary to insert some padding between structure elements or after the last element of a structure. For example, on a 32-bit machine, a data structure containing a 16-bit value followed by a 32-bit value could have 16 bits of padding between the 16-bit value and the 32-bit value to align the 32-bit value on a 32-bit boundary.

Here is a structure with members of various types, totaling 8 bytes before compilation:

struct MixedData
{
    char Data1;
    short Data2;
    int Data3;
    char Data4;
};

After compilation the data structure will be supplemented with padding bytes to ensure a proper alignment for each of its members:

struct MixedData  /* After compilation in 32-bit x86 machine */
{
    char Data1; /* 1 byte */
    char Padding1[1]; /* 1 byte for the following 'short' to be aligned on a 2 byte boundary
assuming that the address where structure begins is an even number */
    short Data2; /* 2 bytes */
    int Data3;  /* 4 bytes - largest structure member */
    char Data4; /* 1 byte */
    char Padding2[3]; /* 3 bytes to make total size of the structure 12 bytes */
};
  • rule: organize struct/class data by "size" to optimize memory usage.

declare same size together, small to larger (MixedData2) to optimize size of MixedData

struct MixedData
{
    char Data1;
    short Data2;
    int Data3;
    char Data4;
};//size = 12
struct MixedData2
{
    char Data1;//1
    char Data4;//1
    short Data2;//2
    int Data3;//4
};//size = 8

reference(Mandarin): https://magicjackting.pixnet.net/blog/post/221968938

pointer & reference

int A[3];// array of 3 ints
int* A[3]; // array of 3 pointers to int
int (*A) [3]; //pointer to an array of 3 int elements
int (&A) [3]; //reference to an array of 3 int elements
Int& [3] ; // x 

int *& p ;// reference to int pointer (int*)
  • Difference bt reference and pointer : reference can’t be changed.

    That’s why there must be assigned an initial value while reference declared.

int a = 1;
int b = 2;

int &ref = a; 
ref = b; // meaning is the same as a=2

int &ref;  // error: no initialization

const char const * const c_ptr;

const char *p; //pointer to constant char
char *const p;  // constant pointer

// constant pointer points to an constant char
const char *const p;
char const *const p;

//find error
const char* p1;	// pointer to const char*
char *const p2;	// error: uninitialized const pointer
p1 = “abc”;
p2 = “abc”;//error: assignment of read-only variable ‘p2’

call by value, call by reference (address)

In the call by value method, a copy of the parameter is passed to the functions. For these copied values a new memory is assigned and changes made to these values do not reflect the variable in the caller. In call by reference method, we pass the address of the variable and the address is used to access the actual argument used in the function call. So changes made in the parameter alter the passing argument.

function pointer

void fun(int a){ cout << a << endl; }

// fun_ptr is a pointer to function fun() 
void (*fun_ptr)(int) = &fun;  

// A function’s name can also be used to get functions’ address. For example, in the below program, we have removed address operator ‘&’ in assignment. We have also changed function call by removing *, the program still works.
void (*fun_ptr)(int) = fun;  

// 2 ways to Invoking fun() using fun_ptr
(*fun_ptr)(10);
fun_ptr(10);

//pass function pointer as argument, usually use "typedef" for readability
typedef void (*fun_ptr)(int);
int call_a_func2(fun_ptr call_this) {
    int output = call_this(7);
    return output;
}
//same as, pass function pointer as argument (not use typedef)
int call_a_func2(int (*call_this)(int)) {
    int output = call_this(7);
    return output;
}

call_a_func2(fun) ;

post-increment ‘itr++’ v.s. pre-increment ‘++itr’ operator.

post-increment ‘itr++’ operator is more expensive than the pre-increment ‘itr’ operator. The post-increment operator generates a copy of the element before proceeding with incrementing the element and returning the copy. Moreover, most compilers will automatically optimize i by converting it implicitly into ++i

C++

difference between struct and class?

Answer: default access level different. struct public members by default, whlie class private members by default

class access specifier public/protected/private

C++ memory allocation

  • operator new/delete: similar to malloc/free in C in allocate raw memory in heap
  • new/delete operator (expression): C++ Memory allocation
    • new operator calls "operator new" to allocate raw memory, then call constructor(ctor).
    • delete operator calls dstor then call "operator delete" to free memory in heap.
X  *a  = new X() ;  // new operator 2 steps: 1) call operator new + 2) call ctor
// X *a = new (operator new(sizeof(X)) X();

delete a ;  // 1) call dstor + 2) call operator delete
// a->~X();
// operator delete(a)


X *a  = new X[2] ; 
// void* buf = operator new[](3*sizeof(X));
// X *a = static_cast<X*>buf;
// for (int i = 0 ; i < 3; ++i) new (a+i) X; // placement new 

delete [] a ;
// for (int i = 0 ; i < 3; ++i) a[i].~X();
// operator delete[](a)

Placement new operator: a special version of operator new that allocates storage from a preallocated, large block of memory (this version already in #include<new>)

New operator syntax with placement new

new (address) (type) initializer

As we can see, we can specify an address where we want a new object of given type to be constructed.

X *a  = new (address) X() ;  // placement new 
delete a ; //no placement delete
X *ar  = new (address) X[3] ;  // placement new 
delete [] ar ; //no placement delete
  • The deallocation is done using delete operation when allocation is done by new but there is no placement delete, but if it is needed one can write it with the help of dstor.
  • In general, placement new means an operator new overload version with extra argument, not required to be allocated address. [Effective C++]

polymorphism

The two types of polymorphism in c++ are:

  1. Compile Time (static) Polymorphism: operation/function overloading including template [Effective C++ #41]
  2. Runtime (dynamic) Polymorphism: virtual function

template

A feature of the C++ programming language (template metaprogramming) that allow functions and classes to operate with generic types. This allows a function or class to work on many different data types without being rewritten for each one.

template<class X>//can replace 'class" keyword by "typename" keyword
X func( X a,X b) {return a;}

overloading V.S. template?

  • Function templates make the function perform the same task for different data types.
  • Function overloading makes the function to performs different task for different parameters you are passing.

difference bt overload & override?

  • override: same func name, choose func depend on object type
  • overload: same func name, choose func depend on diff parameters list

operator overloading is compile-time polymorphism [Effective C++ #41]

In computer programming, operator overloadingis a specific case of polymorphism in which some or all of operators like +, =, or == have different implementations depending on the types of their arguments.


exception handling

a programming language construct designed to handle the occurrence of exceptions, special conditions that change the normal flow of program execution. In general, an exception is handled (resolved) by saving the current state of execution in a predefined place and switching the execution to a specific subroutine known as an exception handler. Depending on the situation, the handler may later resume the execution at the original location using the saved information. For example, a page fault will usually allow the program to be resumed, while a division by zero might not be resolvable transparently.

try {
  // Block of code to try
  throw exception; // Throw an exception when a problem arise
}
catch () {
  // Block of code to handle errors
}
// throw exception could be any type like integer, string or object
throw 0;
throwerror;
throw string(“error”);
throw exc_obj ;  // user-defined exception object, you can also declare exception obj by inheriting std::exception
/* 
exception is catched by type using catch clauses.
The type of the exception object is compared against the exception declaration of each catch clause until type matching, the body of the catch clause is executed.
*/ 
try {
//throw exception; // Throw an exception when a problem arise
} catch (int i) {
    cerr << i <<end;
} catch (const char *msg) {
    cerr << msg <<end;
} catch (string &msg) {
    cerr << msg <<end;
} catch (user_obj &e) {    // any user-defined object
} catch (invalid_argument &e) { // exception object 
    /*standard exception object inherited from std::exception with a member function what() to return error message */
    cerr << e.what() << endl;    
} catch (bad_alloc) { // class type
    /*
    also can match a class type bad_alloc without declare an exception object because we are 
    interested only in catching the exception type and not in actually manipulation the object in catch clause
    */
    cerr << “heap memory exhausted\n” ;
} catch (...) {// any other not catch above 
}

header stdexcept vs exception in c++

  • exception: Defines the base class (i.e., std::exception) for all exceptions thrown by the elements of the standard library, along with several types and utilities to assist handling exceptions.
  • stdexcept : Defines a set of standard exceptions that both the library and programs can use to report common errors.
#include <stdexcept>
  throw invalid_argument("MyFunc argument too large.");

reference: https://stackoverflow.com/questions/25163105/stdexcept-vs-exception-headers-in-c

virtual: dynamic binding

  • Let base class pointer/reference can transparently points to any derived class notes.
  • sequence of calling destructors is from the most derived to base. ex: call destructor from derived class then call destructor of base class to avoid memory leakage

[Effective C++ #7]: Declare destructors virtual in polymorphic base classes.

  • the destruction behavior is undefined if use base pointer points to derived instance. that's why if class is decided to be inherated , dtor should be declared as virtual.
#include <iostream>
using namespace std;
class Base{
public:
    virtual ~Base(){ cout << "Base"<< endl ; }
};
class Derive:public Base{
public:
    ~Derive()  {cout << "Derive"<< endl ;}
};

Base *a = new Derive();
delete a; 
/*
console output :
Derive
Base
*/
  • virtual overhead : there is a virtual pointer in each class pointing to a virtual table.

    not declare any virtual member function if a class is not designed to be inherited - vtpr overhead (additional size of one pointer: 32-bit or 64-bit depends on machine)

  • STL containers are not designed to be inherited, which means there is the destructor is not declared as virtual
  • pure virtual member function : for any Class with ANY pure virtual member function , that’s designed to be as abstract interface (not be instantiated)
class A{
public:
    virtual void foo()=0; // pure virtual
    A(){ cout<<"A "; }
    virtual ~A(){}
};
class B: public A
{
public:
    B(){ cout<<"B ";} 
    ~B(){}
};

A a ; // compile error: cannot declare variable ‘a’ to be of abstract type ‘A’
A *b = new B()  ; // compile error: ctor can’t call A() .

[Class] member functions are defined by compiler if you did not defined

Default constructor, copy constructor, assignment operator ,destructor ,address-of operator if a class does not explicitly declare these member functions, compiler will:

  1. implicitly declare them
  2. implicitly define them , if they are needed.
// empty class is not actual empty 
class X {
// public: 
//     X() ; //Default constructor 
//     X(const X&) ;//copy constructor 
//     X& operator=(const X&) ;//assignment operator
//     ~X();// destructor 
//     X* operator&() ;//address-of operator
    // C++11
    // X(X &&) noexcept = default;            // 4/5: Move Ctor
    // X& operator=(X &&) noexcept = default; // 5/5: Move Assignment    
}; 
X a ; // default ctor  
X b = a; // same as "X b(a)" // copy ctor  
b = a ; // operator=
//  dtor for a and b   
class person
{
    std::string name;
    int age;

    public:
    person(const std::string& name, int age);        // Ctor
    // person(const person &) = default;                // 1/5: Copy Ctor
    // person(person &&) noexcept = default;            // 4/5: Move Ctor
    // person& operator=(const person &) = default;     // 2/5: Copy Assignment
    // person& operator=(person &&) noexcept = default; // 5/5: Move Assignment
    // ~person() noexcept = default;                    // 3/5: Dtor
};

keywords : auto [C++11], extern, mutable, static

  • auto [C++11]: auto allow the compiler deduce the type of a variable from its initializer.

    Initial is necessary. Otherwise, the compiler is not able to know the type.

auto x = 1;// int x = 1;
auto y = sin(1.3);  //double y = sin(1.3);
for(auto it = v.begin() ; it 1= v.end() ;++i) ; 
  • extern: tell the compiler this var or function already be declared in another file (translation unit)
  • mutable: A member data is declared as mutable , that means even a constant member function could change its value.

    for logical constantness [Effective C++]

  • static
void f()
{
    static int i = 0; // is initialed when 1st time execution to this func
    printf(“%d ”,i++);
}
int main()
{
    for(int i=0;i<100;i++) f();
    return 0;
}
//output result: 0 1 2 ...99

type conversion

c style

(Base_C) child_obj ? valid? YES // implicit type conversion
(Child_C) base_obj ? valid? NO

c++ style

function notation

int i = int(0.1);
float f  = int(i);

type conversion operator

  1. static_case:implicit conversions between types (like int to float)
  2. const_cast: remove const
  3. reinterpret_cast: low level conversion like convert pointer to int (platform-dependency)
  4. dynamic_case: save downcase (安全向下轉型) to convert a base ptr to derived ptr. very slow
    • if you only have a base* point to an derived object, you want to execute derived member function , need to converted to derived.
    • [Advanced] typeid operator to provide runtime identification, typeid returns type_info object to represent real class type [Essential C++ P.164]
#include <typeinfo> // typeid operator
class B {
    public:
    virtual string who_am_i ()  { return typeid(*this).name() ; }
}; 
class D:public B {}; 
int test::cast()
{
    B* b = new B();
    cout << b->who_am_i()  << endl ;  //1B
    B* d = new D(); //1B
    cout <<  d->who_am_i()  << endl ;  //1D
    cout  << typeid(*b).name() << typeid(B).name() << endl ; //1B1B
    cout  << typeid(*d).name() << typeid(D).name() << endl ; //1D1D
    return 0 ;     
}

OO and Design

  • 甚麼是《物件導向設計》?

    《物件導向設計》最重要的精神是模組化(Modularity)與簡潔介面必須被封裝(Encapsulation) 抽象(Abstraction)資料型別保證只能被適當的方法操作 類別間的繼承(Inheritance)組合(Composition),把曾經做過的東西加以設定並組合,以減輕工作量,同時減少錯誤發生的機會

  • 封裝 encapsulation Preventing unauthorized access to some piece of information or functionality. The key money-saving insight is to separate the volatile part of some chunk of software from the stable part.

  • 抽象 abstraction The mechanism and practice of abstraction reduces and factors out details so that one can focus on a few concepts at a time. (abstraction - a concept or idea not associated with any specific instance)

  • 繼承 inheritance Inheritance is a way to compartmentalize and reuse code by creating collections of attributes and behaviors called objects which can be based on previously created objects.

  • 多型 polymorphism A programming language feature that allows values of different data types to be handled using a uniform interface.

How to forbid to generate a object in stack?

private constructor or private destructor

//related topic: singleton pattern
class singleton {
public:
    static singleton*  get_singleton() ;
protect :  //if design to be inherited 
    singleton (); // constructor 
private : 
    static  singleton*  _single ; 
}

once-only header ( wrapper #ifndef )

If a header file happens to be included twice, the compiler will process its contents twice. This is very likely to cause an error, e.g. when the compiler sees the same structure definition twice. Even if it does not, it will certainly waste time. The standard way to prevent this is to enclose the entire real contents of the file in a conditional, like this:

/* File foo.  */
#ifndef FILE_FOO_SEEN
#define FILE_FOO_SEEN

//the entire file

#endif /* !FILE_FOO_SEEN */

reference : https://gcc.gnu.org/onlinedocs/cpp/Once-Only-Headers.html


fix or find error

void RemoveHead(node *head)
{
    free(head);
    head = head->next;
}
//fixed: 
void RemoveHead(node *head)
{
    node* tmp = head;
    head = head->next;
    free(tmp);
}
//find error?
class A
{
public:
    A(int a):_x(a),_y(_x){}
    int _y; 
    int _x; 
}

Answer: non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).

char* a = new char[20];
delete a []; // delete [] a
string& print()
{
    string s(“abc”);
    return s; //error : return local var
}
// fix : return object when you should  [Effective C++]
string print()
{
    string s(“abc”);
    return s; 
}