kenics.net

Technical notes on perl, python, php, sql, cgi, c/c++, q/kdb+, unix/shell, revision control tools, data structures & algorithms, and their applications into web services and other various forms of software engineering.

C++ Class OOP

 
######################## 
####  class/object  #### 
######################## 
 
### 
###  class VS object 
### 
 
class is a data structure, a core concept in OOP. 
object is just an instantiation of class. 
 
int usr_val; // int is an integer data structure (class). usr_val is its instanciated object. 
 
 
 
### 
###  initialization 
### 
 
there are two ways. 
 
(1) insetion operator (inherited from C) 
 
int usr_val = 0, age = 2;  // you can define multiple variables with a comma. 
const string greeting = "hello"; 
 
 
(2) constructor 
 
int usr_val (0); 
const string greeting("hello"); 
 
----------------------------------------- 
 
class CParent{ 
   public: 
     CParent(); 
     CParent(int); 
}; 
 
int main(){ 
   CParent cp;                 // calls  CParent::CParent(); 
   CParent cp(123);            // calls  CParent::CParent(int); 
   CParent cp = 123;           // calls  CParent::CParent(int); 
   CParent cp = CParent(123);  // calls  CParent::CParent(int); 
 
   CParent cp();               // this will not work. 
                               // this is just a forward declaration of a function named cp() that returns a CParent object. 

 
----------------------------------------- 
 
btw, the above class is a bit too simplified example. lets make it more realistic as below. 
 
----------------------------------------- 
 
class CParent{ 
   public: 
     CParent(): px(777), vol(999){}   // member variable initialization. 
     CParent(int x) {px = x;}         // here notice we are defining member functions inside class definition 
     int show() {return px;}          // but we can just put a signature, e.g. int show();  instead 
   private:                           // and we define it separately as int CParent::show(){return px;} later 
     int px; 
     int vol; 
}; 
 
----------------------------------------- 
 
instead of     CParent(): px(777), vol(999){}   // this is initialization 
you can do     CParent(){px=777; vol=999}       // this is called assignment, not pure initialization per se 
 
NOTE: you cannot do below. 
 
---------------------- 
class Hoge 

  int var = 100;   // ISO C++ prohibits non-static const member initialization because it does not exist until instantiated. 
}; 
---------------------- 
 
 
###  class VS struct 
 
in C++, only diff btw class and struct is class's default accessor type is private, and struct's default is public. 
the diff btw cpp's class and C's struct is in C, struct can only have variables, pointers, not actual functions. whereas in class of course, you can have functions (which can overload, hence, encapsulation, polymorphysim, inheritance). 
 
in C++, unlike C, you dont need to typedef. 
 
---------  good old C  --------- 
 
struct Node{          // only reserves "struct Node" namespace 
   int num; 
   struct Node *node; 
}; 
 
struct Node *head = malloc(sizeof(struct Node)); 
 
 
---------  C with typedef (ver.1)--------- 
 
typedef struct{         // only reserves "Node" namespace 
   int num; 
   Node *node; 
}Node; 
 
Node *head = malloc(sizeof(Node)); 
 
 
---------  C with typedef (ver.2)--------- 
 
typedef struct Node{         // reserves both "struct Node" and "Node" namespace 
   int num; 
   Node *node; 
}Node; 
 
Node *head = malloc(sizeof(Node)); 
 
 
---------  C++  ----------- 
 
struct Node{                    // as above, C++ struct can have member functions 
   int num;                     // all public 
   string name;                 // both "struct Node" and "Node" namespace reserved 
}; 
 
struct Node ptr1;    // either is fine 
Node ptr2;           // 
-------------------------- 
 
 
 
###  semicolon required at the end of class def. 
 
class CSample {   // just a convention to use a capital C to indicate it's a class. not a must. 
 
} CInstance;    // this semicolon is needed just like you define a struct. ( c-compatibility ) 
                // you can declare an actual instance here too if you want. 
 
 
 
######################## 
###   simple class   ### 
######################## 
 
--------------  main.cpp  --------------- 
#include <iostream> 
 
class CSample 

public: 
        void set(int num); 
      //void set(int);        // this is also ok. 
        int get(); 
private: 
        int m_num; 
}; 
 
int main() 

        CSample obj; 
        int num = 1234; 
        obj.set( num ); 
        std::cout << obj.get() << std::endl; 
 
        return 0; 

 
void CSample::set(int num)             //  :: is a scope operator. 

        m_num = num; 

 
int CSample::get() 

        return m_num; 

---------------------------------------- 
 
=====>  now normally we split the above into three files. 
 
sample.h 
sample.cpp 
main.cpp 
 
-----------  sample.h  ------------- 
 
class CSample 

public: 
        void set(int num); 
      //void set(int);        // this is also ok. 
        void hello(){std::cout << "hello" << endl;}        // you can define like this. but this will be treated as "inline" func. 
                                                           // notice no semi-colon at eol. 
        int get(); 
private: 
        int m_num; 
}; 
------------------------------------ 
 
----------   sample.cpp   ------------ 
 
#include "sample.h"            // assume sample.h is in the same directory 
 
void CSample::set(int num) 

        m_num = num; 

 
int CSample::get() 

        return m_num; 

-------------------------------------- 
 
----------   main.cpp   ------------ 
 
#include <iostream>   // just for std:cout 
#include "stample.h" 
 
int main() 

        CSample obj; 
        int num = 1234; 
        obj.set( num ); 
        std::cout << obj.get() << std::endl; 
 
        return 0; 

 
------------------------------------ 
 
======>  you compile  $  g++ main.cpp sample.cpp 
 
 
############################# 
###   default functions   ### 
############################# 
 
when a C++ class is compiled, compiler automatically creates 4 default functions (even if you dont define them.) 
 
(1) constructor 
(2) destructor 
(3) copy constructor 
(4) assignment operator=() 
 
we will examine each later. 
 
 
######################### 
###   size of class   ### 
######################### 
 
just do sizeof() 
 
but in general 32-bit env, it will be a sum of all member variables. (functions dont matter) 
 
------------------------------------------- 
class CTest 

public: 
    void f(); 
private: 
    int num;    // this will be 4-byte 

------------------------------------------- 
 
===> however, when you use "virtual" the compiler will allocate some space for later enabling polymorphism. known as virtual function table. thus the class size will increase. but exactly how much will depend on compiler. declaring multiple virtual functions will not increase the size of this virtual function table though. (again, depends on compiler) 
 
 
 
###################### 
####   accessor   #### 
###################### 
 
(1) private 
(2) public 
(3) protected               // read inheritance section first 
 
 
class CSample 

    int num;            // private 
  public: 
    inr pub_num; 
    void set(int); 
    int get(){return this->num}       // public member func.   notice how we dont need a semi colon at eol 
};                                    // this->   is optional in this case 
 
struct SSample 

   int num;       // public 
   int set(int);  // public 

 
 
CSample cs; 
CSample *pcs = &cs; 
 
cout << cs.num << endl;       //  error 
cout << cs.get() << endl;     //  ok 
cout << cs.pub_num << endl;   //  ok 
 
cout << pcs->num   << endl;   //  error 
cout << pcs->get() << endl;   //  ok 
cout << pcs->pubnum << enld;  //  ok 
 
 
===>  more intricate examples to come once inheritance, friend, appear 
 
 
####  protected 
 
within a class, protected = private, but from a child class, parent's class's protected means public. 
 
example 
------------------------------------ 
class CParent 

public: 
    int m_public; 
protected: 
    int m_protected; 
private: 
    int m_private; 
}; 
 
class CChild : public CParent 

public: 
        int CallPublic(){ return m_public; }           // ok 
        int CallProtected(){ return m_protected; }     // ok      // so, from child, parent's class's protected = public 
        int CallPrivate(){ return m_private; }         // illegal 
}; 
 
int main() 

        CParent parent; 
 
        parent.m_public = 100;     //  ok 
        parent.m_protected = 100;  //  illegal   // so, within a class, protected = private 
        parent.m_private = 100;    //  illegal 
 
 
        CChild child; 
 
        child.m_public = 123;      //  ok 
        child.m_protected = 123;   //  illegal 
        child.m_private = 123;     //  illegal 
 
        return 0; 

------------------------------------ 
 
 
 
 
 
 
################################# 
####  constructor / overload  ### 
################################# 
 
constructor is a member func that gets called automatically when instantiated. thus often used for initialization. 
constructor has no return type. see below example. 
no need to declare/define if you dont need one. compiler creates an empty one for you. 
 
overload func is where you define the same function name but diff input parameters. (return type can be diff too, but not only return type.) 
(ref) 
http://stackoverflow.com/questions/9568852/overloading-by-return-type 
 
 
CSample cs;     // will call constructor 
CSample *pcs;   // will not call constructor 
 
 
------- tri.h -------- 
class Triangular { 
public: 
    Triangular();                  // no return type unlike other functions 
    Triangular(int len); 
    Triangular(int len, int pos);  // overload funcs 
    void print(); 
private: 
    int _len;                      // one way to denote it's a private member variable with "_" prefix 
    int _pos; 
    int _val; 
    string _name; 
}; 
--------------------- 
// need to define function either (1) inline in tri.h or (2) in some other .cpp file but do #include "tri.h" 
// when defining write like Triangular::print(){ cout << _len << endl;} 
 
Interesting note is 
 
Triangular t1;      // this is a proper instanciation of a Triangular class object 
Triangular t2(7,8)  // correct also 
Triangular t3();    // this is incorrect. to maintain compatibility with C, this has to be a new function that returns a Triangular type data. 
 
 
###  initialization with constructor 
 
class Triangular { 
public: 
... 
    Triangular( int len = 1, int pos = 1 );  // a good way to force initialize 
... 
... 

 
Triangular::Triangular( int len, int pos) // dont forget the scope specifier when defining constructor/member function outside the class 
    : _name("hello_world"), _val(777)     // this is one way to initialize within a constructor 

    _len = len > 0 ? len : 1; 
    _pos = pos > 0 ? pos : 1; 
//  _name = "hello_world";     // this is another way to initialize. but if member variables are const, then you can only do the first way. 
//  _val  = 777;               // either way is fine 

 
===> the above two become important to distinguish when defining "const" constructor. 
===> the above will cover all those three overloading function cases you wrote. 
 
Triangular t1;         // Triangular::Triangular(1,1) 
Triangular t2(5);      // Triangular::Triangular(5,1) 
Triangular t3(12,3);   // Triangular::Triangular(12,3) 
 
 
 
####################### 
####   destructor   ### 
####################### 
 
just add a tilda sign "~" to the class name. 
gets called when a class object is terminated. (e.g. when it reaches the end of the {} scope) 
just like constructor, no need to declare/define if you dont need one. compiler creates an empty one for you. 
 
 
class Matrix{ 
   private: 
      int _row, _col; 
      double *_pmat; 
   public: 
     Matrix( int row, int col ) 
         : _row(row), _col(col) 
     { 
       _pmat = new double[row * col]; 
//     _row = row 
//     _col = col; 
     } 
 
     ~Matrix() 
     { 
        delete [] _pmat; 
     } 
... 
... 

 
=========> look at the below example 
 

  Matrix mat(4,9); // constructor will run 
... 
... 
... 
... 
... 
      // this is where destructor will run. pretty much feels like auto-garbage collection. 
}     // of course you should need destructor in such case as you manually allocating mem space via new 
 
 
 
 
################################ 
####    copy constructor    #### 
################################ 
 
###  shallow copy VS deep copy 
###  when you copy an object, it just copies all its content, including the addresses which member variables point to. 
###  when destructor deletes all the data pointed by those pointers, those get freed up. can be a problem. 
 
----------------------------------- 

   Matrix mat( 3,6 ); 
   { 
      Matrix mat2 = mat;   // member gets copied. called "shallow" copy 
      ... 
      ...                  // destructor for mat2 runs, which runs delete [] mat2._pmat 
   }                       // but mat2._pmat = mat1._pmat   so this will corrupt mat object 
   ... 
   ...                     // this is when we want to delete [] mat._pmat  but it's already corrupted as above 

----------------------------------- 
 
=======> thus you would need to define a copy constructor to do "deep" copy as below 
 
### 
### copy constructor syntax 
### 
 
just like normal constructor but the input arg is a const reference to an instance of the class itself. 
this is obvious because constructor shouldn't be changing the input. 
(e.g.) 
class_name( const class_name& foobar){} 
 
----------------------------------------- 
Matrix::Matrix( const Matrix &rhs)         // copy constructor syntax    just like constructor but (const <classname> &ref) 
  : _row( rhs._row), _col( rhs._col) 

    int elem_cnt = _row * _col; 
    _pmat = new double[ elem_cnt ]; 
 
    for ( int i = 0; x < elem_cnt ; i++) 
        _pmat[i] = rhs._pmat[i];           // here you do a "deep" copy 

----------------------------------------- 
 
 
Matrix mat2 = mat1;       //   this is when copy constructor gets invoked. for mat2, normal constructor is NOT invoked. saves resource. 
 
 
====> but if you do below, only normal constructor gets called. no copy constructor. 
 
Matrix mat2;  // will call normal constructor. 
mat2 = mat1;  // this is no longer initialization. this is just an assignment. NO copy constructor is called. 
 
 
====> this means, if you dont define copy constructor compiler will just create a simple one. 
 
---------------------------------- 
Matrix::Matrix( const Matrix &rhs) 

    *this = rhs; 

---------------------------------- 
 
### 
###  copy constructor and assignment operator 
### 
 
first of all, an object can be assigned value either (1) assignment operator "=" or (2) initialization 
 
for "=", we can do operator=() overload. 
for initialization, we prepare copy constructor. 
 
specifically, there are three initialization cases where copy constructor is invoked. 
(and when copy constructor is called, no normal constructor is called.) 
 
1) declaration of a new class object using an existing object of the same class 
 
 
kstring kstr0("hello");  // normal constructor 
kstring kstr1(kstr0);    // copy constructor 
kstring kstr2 = kstr0;   // copy constructor 
kstr1 = kstr2;           // assignment operator. you need to overload this if you want deep copy. 
 
# this means, a copy constructor is only needed when you need deep copy. 
# if you write a copy constructor, you probably should write overload operator=(); 
# when you need a copy constructor, that means you already have a destructor. 
 
2) passing a class object to a function as an input argument 
 
-------------------------- 
kstring kstr("hello"); 
func(kstr);             // copy constructor is called 
                        // it's a bit obvious when you think about it. you dont want x.~kstring() to delete kstr's pointer. 
---- 
void func(kstring x){ 
   std::cout << x.get() << std::endl; 

-------------------------- 
 
 
3) function return value 
 
----------------------- 
kstring kstr = func(); 
------- 
kstring func(){ 
   kstring kstr; 
   return kstr; 

----------------------- 
 
# in c++11, instead of copy constructor, move constructor is used. 
# also, there is "copy constructor elision" concept where return value optimization allows copy constructor to be skipped. 
# see below ref link and example code. 
 
(ref) 
http://en.wikipedia.org/wiki/Copy_constructor 
http://www.fredosaurus.com/notes-cpp/oop-condestructors/copyconstructors.html 
http://stackoverflow.com/questions/20299153/copy-constructor-is-called-when-returning-object-from-a-function 
http://stackoverflow.com/questions/8890528/copy-constructor-elision 
http://homepage2.nifty.com/assua/aosite/cpp/construct_des/cpb2.html 
http://homepage2.nifty.com/assua/aosite/cpp/clas_x/cpb5.html 
 
 
# example code 
 
-------------------------------------------- 
 
#include <iostream> 
#include <cstring> 
using namespace std; 
 
class CParent 

public: 
  CParent() 
  { 
    cha = new char[10]; 
    cha[0] = 'a'; 
    cha[1] = 'b'; 
    cha[2] = 'c'; 
    cha[4] = 0; 
    cout << "normal constructor called" << endl; 
  } 
 
  CParent(const CParent& cp) 
  { 
    int len = strlen(cp.get()); 
    cha = new char[len+1]; 
    for(int i = 0; i < len+1 ; i++) 
      cha[i] = cp.at(i); 
    cha[len] = 0; 
    cout << "copy constructor called" << endl; 
  } 
 
  ~CParent() 
  { 
    cout << "destructor called. " << get() << endl; 
    delete [] cha; 
  } 
 
  void operator=(const CParent& rhs) 
  { 
    cout << "operator= overload called" << endl; 
    int len = strlen(rhs.get()); 
    delete [] cha; 
    cha = new char[len + 1]; 
    for(int i = 0; i < len+1 ; i++) 
      cha[i] = rhs.at(i); 
    cha[len] = 0; 
  } 
 
  void set(const char c){cha[0] = c;} 
  char* get() const {return cha;} 
  char at(int x) const {return cha[x];} // no input validation 
 
private: 
  char* cha; 
}; 
 
void f(CParent cp) 

  cout << "f() is called" << endl; 
  cout << cp.get() << endl; 
  cp.set('f'); 

 
void g(CParent& cp) 

  cout << "g() is called" << endl; 
  cout << cp.get() << endl; 
  cp.set('g'); 

 
CParent h() 

  cout << "h() is called" << endl; 
  CParent tmp; 
  tmp.set('h'); 
  return tmp; 

 
CParent& z()     // a bad example. it's dangerous returning a ref to a temp obj 
{                // which gets destroyed once the func exits 
  CParent tmp; 
  tmp.set('z'); 
  return tmp; 

 
int main() 

  CParent cp0;                     // normal constructor called 
 
  cout << cp0.get() << endl;         //  abc 
 
  CParent cp1 = cp0;               // copy constructor called (case 1) 
  //CParent cp1(cp0);              // same 
 
  cp1.set('1'); 
  cout << cp1.get() << endl;       // 1bc 
 
  cout << "gonna call f()" << endl; 
  f(cp1);                          // copy constructor called (case 2) 
                                   // destructor called. fbc 
  cout << "f() ended" << endl; 
  cout << cp1.get() << endl;       // 1bc 
 
  cout << "gonna call g()" << endl; 
  g(cp1); 
  cout << "g() ended" << endl; 
  cout << cp1.get() << endl;       // gbc 
 
 
  CParent cp2;                     // normal constructor called 
  { 
    CParent cp3;                   // normal constructor called 
    cp2 = cp3;                     // operator= overload called 
    cp2.set('2'); 
    cout << cp2.get() << endl;     // 2bc 
    cp3.set('3'); 
  }                                // destructor called. 3bc 
 
  cout << cp2.get() << endl;       // 2bc   // proves deep copy operator=() worked for cp2=cp3 
 
 
  CParent cp4;                     // normal constructor called (for cp4) 
  cp4 = h();                       // normal constructor called (for tmp)  (also operator= overload called) 
                                   // destructor called. hbc  (for tmp) 
  cp4.set('4'); 
  cout << cp4.get() << endl;       // 4bc   (again, thanks to operator=() overload, cp4 is not corrupt) 
 
  CParent cp5 = h();               // normal constructor for tmp in h(), then there should be copy constructor for cp5 
  cp5.set('5');                    // but this may be omitted due to "copy constructor elision" 
  cout << cp5.get() << endl;       // in that case, the destructor for tmp(hbc) is never called, and only cp5(5bc) will be called at the end. 
 
  return 0; 
}                                  // destructor called. 5bc (for cp5) 
                                   // destructor called. 4bc (for cp4) 
                                   // destructor called. 2bc (for cp2) 
                                   // destructor called. gbc (for cp1) 
                                   // destructor called. abc (for cp0) 
 
 
-------------------------------------------- 
 
 
 
 
 
 
################################ 
#####     inheritance     ###### 
################################ 
 
inheritance lets you define "is-a" relationship between objects.  very closely tied with encapsulation & polymorphism. 
effective with override. 
 
"association" is where two classes are associated, e.g. a class Teacher and another class Student, in their definitions, there are some parts where they use each other. but their life-cycle is independent from each other. 
 
if you declare a class within another class, it's "has-a" relationship(aka aggregation/composition). 
 
aggregation is a sub-set of association where a house has a human. a human cannot belong to two houses simultaneously. when the house dies, the human can live and move to other house. 
composition is a sub-set of aggregation. aka death relationship, like house and rooms. when a house goes down, all rooms go down. 
 
(ref) 
http://www.codeproject.com/Articles/330447/Understanding-Association-Aggregation-and-Composit 
 
 
 
#####   inheritance 
 
-------------------------------------- 
#include <iostream> 
using namespace std; 
 
class CSuper 

  public: 
    CSuper(){super_num=777;} 
    int f(){return super_num;} 
  private: 
    int super_num;                     // only accessible within the class or via public member func that lets child class to manipulate remotely. 
}; 
 
class CChild : public CSuper 

public: 
  CChild(): child_num(333){} 
  int g(); 
private: 
  int child_num; 
}; 
 
int CChild::g(){ 
  return this->child_num;              // cannot access super_num cos it's private within the parent class 

 
int main() 

  CChild ch; 
  cout << ch.g() << endl;              // will print 333 
  cout << ch.f() << endl;              // will print 777 
  //  cout << ch.child_num << endl;    // cant access private 
  //  cout << ch.super_num << endl;    // cant access private 
  return 0; 

------------------------------------------ 
 
 
 
#####   function override 
 
override - you can override the definition of a function from a parent class in a child class. but NEVER change the input parameter. return type can be changed. function name and input param types are known as "signature" of function. note that return type is not included in the signature. 
 
(ref) 
http://www33.ocn.ne.jp/~loreley/FSB/cpp02.html 
http://d.hatena.ne.jp/mickey24/20100103/1262471661 
http://homepage2.nifty.com/well/OverloadOverride.html 
 
------------------------------------------------------------ 
#include <iostream> 
using namespace std; 
 
class CParent 

public: 
  float f(int x){return x;} 
}; 
 
class CChild : public CParent 

public: 
  double f(int x){return x*2;} 
}; 
 
int main() 

  CParent cp; 
  CChild ch; 
  cout << cp.f(555) << endl;            // will print 555 
  cout << ch.f(111) << endl;            // will print 222 
  cout << ch.CParent::f(123) << endl;   // will print 123   // if you identify the scope explicitly like this 
                                                            // you can change input param of f() 
  return 0; 

-------------------------------------------------------------- 
 
 
######   virtual function 
 
 
---------------------------------------------------------------------- 
#include <iostream> 
using namespace std; 
 
class CParent 

public: 
  virtual float f(int x){return x;}     // if you remove "virtual" it will compile, but the below output will be diff. 
}; 
 
class CChild : public CParent 

public: 
  float f(int x){return x*2;} 
}; 
 
int main() 

  CParent* cp = new CParent; 
  CChild* ch = new CChild; 
  CParent* cp2 = new CChild;        // yes this works, hence the polymorphism 
  cout << cp->f(555) << endl;       // will print 555 
  cout << ch->f(111) << endl;       // will print 222 
  cout << cp2->f(123) << endl;      // will print 246    //  if no "virtual" then this will print 123 
  return 0; 

--------------------------------------------------------------------- 
 
=======> in virtual func, return type must be the same or "covariant" which essentially means pointers/references to classes. as in CParent* and CChild* 
 
(ref) 
http://stackoverflow.com/questions/4665117/c-virtual-function-return-type 
 
=======> in recap, polymorphism happens with (1) member function override, and (2) parent class's pointer|reference is used. 
 
NOTE:  one exception is when you call a virtual function in a parent's class constructor. since child class is not instantiated yet, the constructor will use the func from the parent class function. 
 
 
#####   interface class (aka interface, abstract class) 
 
interface class should have ONLY member function declaration. NO definition. also, NO member variables. 
because only function declaration, interface class cannot be instantiated. 
 
the expectation is such that it's meant to be inherited, and functions to be defined in an overridden way. 
 
----------------------------- 
class ISample   // instead of CSample, I is for interface 

public: 
    virtual int f(int,int) = 0;    //  this =0 forces the member func to be "pure virtual function" which allows you to skip func definition. 
};                                 //  also explicitly restrict the instantiation of this interface class 
 
----------------------------- 
 
========>  the rest is just the same as inheritance/override as above. 
 
 
 
#####   Aggregation 
 
--------------   sample.h   ------------ 
#include <iostream> 
 
class CSuper 

public: 
        CSuper(); 
        virtual ~CSuper(); 
        void Func(); 
}; 
 
class CInner 

public: 
        CInner(); 
        virtual ~CInner(); 
        void Func(); 
}; 
 
class CSub : public CSuper 

public: 
        CSub(); 
        virtual ~CSub(); 
        void Func(); 
 
private: 
        CInner m_inner;             // see aggregation/composition 
}; 
 
 
------------   sample.cpp    ------------ 
 
#include "sample.h" 
 
CSuper::CSuper() 

        std::cout << "super class constructor" << std::endl; 

 
CSuper::~CSuper() 

        std::cout << "super class destructor" << std::endl; 

 
void CSuper::Func() 

        std::cout << "super class func" << std::endl; 

 
CInner::CInner() 

        std::cout << "inner class constructor" << std::endl; 

 
CInner::~CInner() 

        std::cout << "inner class destructor" << std::endl; 

 
void CInner::Func() 

        std::cout << "inner class func" << std::endl; 

 
CSub::CSub() 

        std::cout << "sub class constructor" << std::endl; 

 
CSub::~CSub() 

        std::cout << "sub class destructor" << std::endl; 

 
void CSub::Func() 

        std::cout << "sub class func" << std::endl; 
        CSuper::Func(); 
        m_inner.Func(); 

 
------------------------   main.cpp  --------------------- 
#include "sample.h" 
 
int main() 

        CSub obj; 
 
        obj.Func(); 
        return 0; 

 
----------------------   output   ----------------------- 
 
super class constructor 
inner class constructor 
sub class constructor 
sub class func 
super class func 
inner class func 
sub class destructor         // the order is just the opposite of constructor 
inner class destructor 
super class destrcutor 
 
 
 
 
 
 
############################## 
##   private inheritance    ## 
############################## 
 
------------------------------- 
class CChild : public CParent       // if you omit "public" then defaults to "private" in class 

}; 
 
struct SChild : private CParent    // struct's default inheritance access level is "public" 

}; 
-------------------------------- 
 
private inheritance means, all member var/func of the parent class will appear private to an instance of a child class. but within the child class definition, member functions can still access public|protected members of the parent class. 
 
e.g. 
------------------------------------- 
#include <iostream> 
using namespace std; 
 
class CParent 

  int foo; 
public: 
  CParent(): foo(123){} 
  int getfoo() const {return foo;} 
}; 
 
class CChild : private CParent 

public: 
  int getval() const { return getfoo();}   // legal 
}; 
 
int main() 

  CChild ch; 
 
  cout << ch.getval() << endl;            // legal 
  cout << ch.getfoo() << endl;            // error    this was ok in public inheritance 
 
  return 0; 

--------------------------------------- 
 
### NOTE: 
- another important aspect of "private" inheritance is a child class cannot be treated as a parent class type. 
i.e private inheritance is not a "is-a" relation, it's "is-implemented-in-terms-of" relation. 
 
e.g. 
--------------------------------------- 
#include <iostream> 
using namespace std; 
 
class CParent{}; 
class CChild : private CParent {}; 
 
int main() 

  CParent cp; 
  CChild ch; 
 
  CParent* cpp1 = &cp; 
  CParent* cpp2 = &ch;           //  error.  this is ok in public inheritance. 
 
  return 0; 

---------------------------------------- 
 
 
 
################################## 
###   protected inheritance    ### 
################################## 
 
- from child class's perspective, protected inheritance turns the parent class's both public & protected members into protected type, while private members remain private. 
 
--------------------------------------- 
#include <iostream> 
using namespace std; 
 
class CParent 

public: 
  int pub_num; 
protected: 
  int pro_num; 
private: 
  int pri_num; 
}; 
 
class CChild : protected CParent 

public: 
  int getpub(){return pub_num;} 
  int getpro(){return pro_num;} 
  int getpri(){return pri_num;}  // error 
}; 
 
int main() 

  CChild ch; 
 
  cout << ch.getpub() << endl;  // legal 
  cout << ch.getpro() << endl;  // legal 
  cout << ch.getpri() << endl;  // error 
 
  cout << ch.pub_num << endl;   // error 
  cout << ch.pro_num << endl;   // error 
  cout << ch.pri_num << endl;   // error 
 
  return 0; 

---------------------------------------- 
 
 
 
##################################### 
#####   multiple inheritance    ##### 
##################################### 
 
you can do this. 
 
------------------------------------------------- 
class CChild : public CParent1, public CParent2   // dont forget the second explicit public for CParent2 

}; 
------------------------------------------------- 
 
=======> if you write as below, CParent2 will be inherited as private. 
 
------------------------------------------------- 
class CChild : public CParent1, CParent2 

}; 
------------------------------------------------- 
 
 
 
#####    typical problem with multiple inheritance (1) 
 
see the below code. 
 
----------------------------------------------- 
#include <iostream> 
using namespace std; 
class CParent1 

public: 
    virtual int f(){return 777;} 
}; 
 
class CParent2 

public: 
    virtual double f(){return 1.23 ;} 
}; 
 
class CChild : public CParent1, public CParent2 

}; 
 
 
int main() 

    CChild* ch; 
    int a = ch->f();       //  compile error.  this cannot resolve which f() to execute. 
    return 0; 

----------------------------------------------- 
 
=======>  so you end up calling  ch->CParent1::f()  which will work, but defeats the benefit of polymorphism. 
 
 
 
#####    typical problem with multiple inheritance (2) 
 
class CParent{}; 
class CMiddle1 : public CParent{}; 
class CMiddle2 : public CParent{}; 
class CChild : public CMiddle1, public CMiddle2{}; 
 
=====> assuming CParent class has some public member variable int var, what happens when you do below? 
 
CChild ch; 
ch.var = 777;    // it cannot resolve which CParent, as there is one for CMiddle1, and one for CMiddle2 
 
ch.CParent::var == 777   //  still will not help. 
 
 
======> to resolve, you define as below. so both CMiddle1 and CMiddle2 will inherit one same CParent. 
 
class CParent{}; 
class CMiddle1 : public virtual CParent{};            // added 'virtual' in inheritance declaration 
class CMiddle2 : public virtual CParent{}; 
class CChild : public CMiddle1, public CMiddle2{}; 
 
CChild ch; 
ch.var = 666;  // this works now. 
 
 
 
 
 
################################# 
#####      friend           ##### 
################################# 
 
there are two kinds :  frined class and friend function 
 
(1) frined class 
 
a class or a function that has been declared "friend" can access all member func/var including private ones. 
 
---------------------------------------------- 
#include <iostream> 
using namespace std; 
 
class CFriend;                  // you need a forward declaration 
                                // (if it's a forward declaration of a function, it's called function prototype) 
 
class CFoo 

  friend class CFriend;         // you can define this anywhere within CFoo, but putting it at the beginning makes it easier to spot. 
  public: 
    CFoo(int x) : privar(x) {} 
  private: 
    int privar; 
}; 
 
class CFriend 

  public: 
     int getCFooPrivateMemberVar(CFoo& cf); 
}; 
 
int CFriend::getCFooPrivateMemberVar(CFoo& cf) 

  return cf.privar;             // notice how it's accessing a private member var from CFoo class. 

 
int main() 

  CFoo cf(777); 
  CFriend cfriend; 
 
  cout << cfriend.getCFooPrivateMemberVar(cf) << endl;    //  777 
 
  return 0; 

--------------------------------------------- 
 
 
(2) friend function 
 
this is to let a global function access private member properties of a class that acknowledge the global function as friend. 
 
------------------------------------------------- 
#include <iostream> 
using namespace std; 
 
class CFoo 

  friend int getCFooPrivateVar(CFoo& cf);      // this func will be able to access all member properties of CFoo 
                                               // dont confuse this as a member func of CFoo class btw. 
public: 
  CFoo(int x) : privar(x) {} 
private: 
  int privar; 
}; 
 
int getCFooPrivateVar(CFoo& cf)                // it's a global function, not a member func of some other class. 

  return cf.privar; 

 
 
int main() 

  CFoo cf(777); 
 
  cout << getCFooPrivateVar(cf) << endl;      //  777 
 
  return 0; 

-------------------------------------------------- 
 
 
####   NOTE:  friend effect and inheritance   #### 
 
- if you define a class A that inherits another class B that has friend declaration, then the subclass A will not have the friend effect to whatever class/func which the parent class B allowed. 
 
 
 
########################################################### 
###   header file (.h) and implementation file (.cpp)   ### 
########################################################### 
 
just like we do with C, we can use the usual .h & .cpp convention. 
in .h, you define a class, and you define member functions in .cpp. 
 
e.g. 
 
----------  Date.h  ----------- 
 
#ifndef DATE_H 
#define DATE_H 
#include <string> 
using std::string;   // this will extend to Date.cpp as well 
                     // NEVER a good idea to set "namespace" inside a header file 
class Date 

public: 
    Date(): m_year(2099), m_month(12), m_day(31){}   // init 
    Date(int year, int month, int day); 
    void setDate(int year, int month, int day); 
    int getYear()  { return m_year; } 
    int getMonth() { return m_month; } 
    int getDay()   { return m_day; } 
    string getMonthStr(); 
    void print(); 
private: 
    int m_year; 
    int m_month; 
    int m_day; 
}; // dont forget this semicolon !!!!!!! 
 
#endif 
 
------------------------------- 
 
NOTE: notice above we actually implemented get{Year,Month,Day}() member funcs and Date() constructor because they are so short. 
      but other mem funcs are not yet implemented, that's what .cpp file is for. 
 
----------  Date.cpp  ---------- 
 
#include "Date.h" 
#include <string>    // std::string 
#include <iostream>  // std::cout, std::cin 
using namespace std; 
 
Date::Date(int year, int month, int day) 

  setDate(year, month, day); 

 
void Date::setDate(int year, int month, int day) 

  m_month = month; 
  m_day = day; 
  m_year = year; 

 
string Date::getMonthStr() 

  string monthStr = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; 
  return monthStr[m_month-1]; 

 
void Date::print(){ 
  cout << m_year << m_month << m_day << endl; 

 
-------------------------------- 
 
then you usually have some test .cpp that contains int main() 
 
--------  testDate.cpp --------- 
 
#include <iostream> 
#include <string> 
using namespace std; 
 
#include "Date.h" 
int main() 

  cout << "lets test Date.h & Date.cpp" << endl; 
 
  Date dt1; 
  dt1.print();           // 20991231 
 
  Date dt2(1998, 03, 17); 
  dt2.print();           // 19980317 
 
  cout << "bye" << endl; 
 
  return 0; 

--------------------------------

  1. 2014-05-08 00:11:01 |
  2. Category : cpp
  3. Page View:

Google Ads