When the C++ program is compiling, it will divide the memory into four partitions
- Coding area: Save the binary code of the functions, managed by operation system
- Global zone: Save the global variables, static variables and constants
- Stack area: Automatically allocated and released by the compiler
- Heap area: Allocated and released by the user, or it will be retrieved by the operation system if the user didn't release it
After the program is compiled, it will become an exe. executable program, before the execution, it is divided into two area
Coding area:
- Storage the machine inductions executed by CPU
- The coding area is shared, especially for the program executed frequently, only once piece of code in the memory is fine
- The coding area is read-only, which is to prevent any accident that modifying its inductions
Global zone:
- Global variables and static variables are storage here
- Global zone also includes the constant area, string constant and other constant are saved here as well
- After finishing the programming, the data in this area is released by the operation system
#include<iostream>
using namespace std;
// Global variables
int glo_a = 10;
// Constant decorate global variable
const int c_g_a = 10;
int main()
{
// Create local variables
int a = 10;
// Create static variables
static int s_a = 10;
// Constant
// String constant
cout<< "String const address"<<(int)&"hello world"<<endl;
// Constant decorate
// COnstant decorate local variable
const int c_l_a = 10;
// Check the addres distance betwee the global and local variables
cout<<(int)&a<<endl;
cout<<(int)&glo_a<<endl;
}
Summary
- Before the execution, C++ are divided into global area and coding area
- The characters of coding area is shared and read-only
- The global area includes global variables, static variables and constant
- The constant area includes global constant and string constant which decorated by const
Stack Area
- Allocated and released by the compiler, save the parameter value of the function and local variables
- Don't return the address of local variables, the data developed by the stack area will be released by the compiler
int *func()
{
int a = 10; // Local variable, saved in stack area, released automatically by compiler after execution
return &a; // Return the Local variabel address
}
int main()
{
int *p = func();
cout<*p<<endl; //In the first time it can print the correct num, as the compiler save only once
cout<<*p<<endl; // When second time calling, it is no longer saved
}
Heap Area
- Allocated and released by the user, or it will be retrieved by the operation system if the user didn't release it
- In C++, generally using new to develop memory
int *func()
{
// Using new keyword, we ncan develop the data into heap area
int *p = new int (10); // Pointer is local variable as well,located in stack, the data stored by pointer is put in the heap area
return p;
}
int main()
{
int *p = func();
cout<<*p<<endl;
return 0;
}
Usage: Using new to develop data in heap area.
Data in heap area is developed and released by users, when deleting, using symbol "delete"
Data created by new, returns the pointer with corresponding data type
Grammar: new data type
int *func()
{
int *p = new int(10);
return p;
}
void test01()
{
int *p = func();
cout<<*p<<endl;
// To release the memory
delete p;
}
// Develop array in heap area
void test02()
{
int *arr = new int[10];
for (int i=0; i<10; i++)
{
arr[i] = i+10;
}
for (int i=0; i<10;i++)
{
cout<<arr[i]<<endl;
}
//Release array in heap area
delete[] arr;
}
int main()
{
test01();
test02();
return 0;
}
Usage: Giving the variables another names
Grammar: datatype &new_name = original_name
#include<iostream>
using namespace std;
int main()
{
// Define a variable
int a = 10;
// Pointer
int *p = &a;
// Citation
int &b = a;
// Pring the address, three results are the same
cout<<&a<<endl;
cout<<p<<endl;
cout<<&b<<endl;
return 0;
}
Note
- Citation must be initialized
- After the initialization, the citation is no longer being changed
Usage: When transferring parameters in functions, we can use citation to decorate actual parameters by formal parameters.
Benefits: Simplify the pointer for decorating the actual parameters
void mySwap03(int &a, int &b)
{
int temp = a;
a = b;
b = a;
}
int main()
{
int a = 10;
int b = 20;
mySwap03(a,b); // Foraml p will changed the actual p
return 0;
}
Usage: Citation can be the return value of the functions
Note: Don't return the local variable citation
int& test01()
{
int a = 10; // Local variable is stored in the stack area
return a;
}
// Calling function can be the left value
int& test02()
{
static int a = 10; // Static variable, stored in the global area, released by the operation system after code is compiled
return a;
}
int main()
{
int &ref = test01();
// test02 case
test02() = 100; // If the return of a function is a citation, the calling of funciton can be the left value
return 0;
}
The nature of Citation in C++ is a pointer constant
void func(int& ref)
{
ref = 100; // *ref = 100
}
int main()
{
int a = 10;
int& ref = a; // Automaticly transferred to int* const ref= &a; The direction of Pointer const cannot be changed, thus the citation cannot be changed in address.
ref = 20;
}
Usage: Const Citation is designed to decorate formal parameters, to avoid any wrong operations
In function formal parameter list, we can add const to decorate formal parameters, to avoid the formal parameters changing actual parameters
void showValue(const int& val)
{
cout<<val<<endl;
}
In C++, the formal parameters of the function can be a default value
Grammar: Return_type Function_name (parameter = default_value) {}
int func(int a, int b, int c = 10)
{
return a+b+c;
}
// If some position has the default parameters, then the foramal parameters are supposed to have the default value after that position
// If the call of the function has default parameters, the parameters in statement of the function can not be changed
int main()
{
int a = 10;
int b = 10;
cout<<func(a, b, 10)<<endl;
return 0;
}
In C++, the formal parameter list can have placeholder parameters, when calling the function, it is supposed to define that place
Grammar: Return_datatype Function_name (datatype) {}
Usage: The name of functions can be the same, increasing the re-use times
Conditions:
- They are in the same scope
- The name of those functions are the same
- The parameters can be different in data_type, numbers, sequences between functions
Note: The return value of the function can not be the condition for function overloading
void func()
{
cout<<"Test"<<endl;
}
void func(int a) // Parameters number different
{
cout<<"Test02"<<endl;
}
void func(int& a)
{
cout<<"Test1"<<endl;
}
void func(const int&a)
{
cout<<"Test2"<<endl;
}
// Parameters types different
// When the function overloading with default parameters
void func2(int a, int b = 10)
{
cout<<"Test3"<<endl;
}
void func2(int a)
{
cout<<"Test3"<<endl;
}
int main()
{
int a = 10;
func(a); // This will use the first function
func(10); // This will use the second function as int& a is illegal.
func2(10); // This is wrong, both func2 can work. We should avoid
return 0;
}
Three characterizes in C++ Class and Object: Encapsulation, Inheritance, Polymorphism
Usage:
- Integrate the attribute and behavior to describe the objects
- Limiting the authority of the attribute and behavior
Grammar: class class_name{access_authority: attribute / behavior};
// PI
const double PI = 3.14;
// Design a Circle class to calculate the perimeter
// Formula: 2*pi*r
class Circle
{
// Access authority
public: // Public authority
// Attribute - Radius
int m_r;
// Behavior - Calculate the perimeter
double calculateZC()
{
return 2*PI*r;
}
}
int main()
{
// Create a circle instance
Circle c1;
// Assign value
c1.m_r = 10;
cout<<"Circle Perimeter"<<c1.calculateZC()<<endl;
return 0;
}
Note
Attribute and Behavior are named members in a Class
Usage 2
Limiting the authority of the attribute and behavior
Three types of authority:
- Public - both inner and outer class can access
- Protected - Only inner class can access
- Private - Only inner class can access
class Person
{
public:
// Public
string name;
protected:
// Protected
string car;
private:
// Private
int password;
public:
void func()
{
name = "Jack";
string = "Audi";
password = 123;
}
}
int main()
{
// Instance
Person p1;
p1.name = "Tom"; // Correct, we can change
p1.car = "Benze"; // Wrong, only inner class can change
p1.password = 124; // Wrong, only inner class can change
return 0;
}
The only difference is the access authority
- struct access authority is public as default
- class access authority is private as default
class C1
{
int a; // default as private
}
struct C2
{
int a; // default as public
}
Usage:
- By setting private member attribute, the user can control the read and write authority
- For the write authority, we can check dataset is working or not
class Person
{
public:
// give the nmae
void give_name(string name)
{
p_name = name;
}
// get the name
void get_name()
{
return p_name;
}
private:
// Name -- read & write
string p_name;
// Age -- read only
int p_age;
// Partner -- write only
int p_partner;
}
The initialization and cleaning of the object are two significant problems:
- An object or a variable without initial status, the result of using it is unknown
- After access an object or a variable without releasing in time, which can cause some security problems
To solve that, C++ provides Constructor & Destructor to initialize and clean work. The initialization and cleaning of objects are the processed in force, if the user doesn't provide C & D, the compiler will do it but with NULL achieve.
Constructor
Usage: Giving the initial value when creating object and members, it is compiled by the compiler automatically
Grammar: class_name () {}
- Constructor has no return value and no void
- The name of function is the same as the class name
- Constructor can have overload since it can have parameters
- Compiler will call it automatically and only work once
Destructor
Usage: Processing the information cleaning work, it is called before the object deleted by the system automatically
Grammar: ~class_name () {}
- Destructor has no return value and not void
- The name of the function is the same as the class name and plus ~
- Destructor can not overload since it can not have parameters
- Compiler will call it automatically and only work once
class Person
{
public:
// Constructor
Person()
{
cout<<" The calling of the constructor"<<endl;
}
// Destructor
`Person()
{
cout<<"The calling of the descructor"<<endl;
}
}
Two categories:
-
Based on the parameters: Args constructor & No-args constructor
class Person { public: Person() { cout<<"No-args constructor"<<endl; } Person(int a) { cout<<"Args constructor"<<endl; } }
-
Based on the types: General constructor & Copy constructor
class Person { public: Person(const Person &p) // Citation used in calling { age = p.age; cout<<"Copy constructor"<<endl; } private: int age; }
Three call methods:
-
Bracket method
int main() { Person p1; //Default constructor call Person p2(10); // Args C call Person p3(p2); // Copy C call // Note: when calling the default constructor, do not add () bracket. This can confuse the compilier as it thought this would be a declaration of a function Person p1(); return 0; }
-
Display method
int main() { Person p1; Person p2 = Person(10); // Args C Person p3 = Person(p2); // Copy C Person(10); // Anonymous object, after the compiling, the system will collect it automatically // Note: Do not use the copy C to initialize the anonymous object, since comiplier will think Person (p3) = Person p3 which is creating a instance Person (p3); return 0; }
-
Implicit conversion
int main() { Person p4 = 10; Person p5 = p4; return 0; }
Three situation we can use copy C:
- Initializing a new object by a well-created object
- Transferring value to the function parameters based on the value transferring
- Return to the local object as value
class Person
{
public:
Person()
{
cout<<"Constructor"<<endl;
}
Person(int age)
{
m_age = age;
cout<<"Args C"<<endl;
}
Person(const Person &p)
{
m_age = p.age;
cout<<"Copy C"<<endl;
}
~Person()
{
cout<<"Destructor"<<endl;
}
private:
int m_age;
}
// 1. Initializing a new object by a well-created object
void test01()
{
Person p1(20);
Person p2(p1);
}
// 2. Transferring value to the function parameters based on the value transferring
void doWork()
{
}
void test02()
{
Person p;
doWork(p;)
}
// 3. Return to the local object as value
Person doWork2()
{
Person p1;
cout<<(int*)&p1<<endl;
return p1;
}
void test03()
{
Person p = doWork2();
cout<<(int*)&p<<endl;
}
Generally, C++ compiler has three functions for a class at least:
- Default constructor (no parameters, NULL statement)
- Default destructor (no parameters, NULL statement)
- Default copy constructor, to copy the attribute
Rules for calling the constructor:
- If the user has defined the constructor, C++ will use the defined one, but provide the default copy constructor
- If the user has defined the copy constructor, C++ will no longer provides any other constructors
Deep copy: apply new space in heap area, then perform copy operation
Shallow copy: simple assign value copy operation
class Person
{
public:
Person()
{
cout<<"No-args C"<<endl;
}
Person(int age, int *height)
{
p_age = age;
p_height = new int(height);
cout<<"Args C"<<endl;
}
Person(const Person &p)
{
p_age = p.p_age;
p_height = p.p_height; // Generated by compiler, shallow copy
// Deep copy
p_height = new int(*p.p_height); // Apply a new memory space in heap area
cout<<"Copy C"<<endl;
}
~Person()
{
if (m_height != NULL)
{
delete m_height;
m_height = NULL;
}
cout<<"Destructor"<<endl;
}
private:
int p_age;
int *p_height;
}
The problem of the shallow copy is: the memory space in heap are will be released in repeat.
To solve it, we use deep copy.
Usage: Initialize the attribute
Grammar: constructor(): att1(val1), att2(val2)... {}
class Person
{
public:
Person(int a, int b, int c)
{
p_a = a;
p_b = b;
p_c = c;
}
// Initialize list
Person(int a, int b, int c): p_a(a), p_b(b), p_c(c){}
private:
int p_a;
int p_b;
int p_c;
}
The member of a Class can be the object defined by another Class: Object-members
class Phone
{
public:
Phone(string pho_name)
{
ph_name = pho_name;
}
string ph_name;
}
class Person
{
public:
Person(string name, string phone): p_name(name), p_phone(phone)
string p_name;
Phone p_phone;
}
Note:
Construct: When the object (from Class A) is the member in Class B, when creating Class B, it will construct Class A object first then Class B.
Destruct: The process is opposite with the construct, it will first destruct the Class B, then destruct Class A.
static function in a class
Static members includes:
-
Static member variables
All the object share the same dataset
Allocating memory space in compiling stage
Statement in the class, initializing out of the class
class Person { public: // Statement in the class static int p_a; private: // Static member variable has the access authority as well static int p_b; } // initializing out of the class int Person::p_a = 100; int Person::p_b = 200; // Wrong, we cannot access it void test02() { // Static member variable is not belong to a certain object, all the objects share the same dataset // Static member variable has two access methods //1. Access by the object Person p; cout<<p.p_a<<endl; //2. Access by the calss name cout<<Person::p_a<<endl; }
-
Static member functions
All the object share the same function
Static member function can only access static member variables
class Person { public: static void func() { p_a = 100; // Correct, static function can access static variable p_b = 200; // Wrong, static function cannot access other variable cout<<"Static function test"<<endl; } static int p_a; int p_b; private: static void func2() { cout<<"Test"<<endl; } } int Person::p_a = 0; // Access void test01() { // 1. Access by object Person p; cout<<p.func()<<endl; // 2. Access by the class name cout<<Person::func()<<endl; cout<<Person::func2()<<endl; // Wrong, private cannot be accessed }
The member variable and member function are stored separately
Only non-static member variables are belong to the object of the class
class Person
{
int p_a;
static int p_b;
void func(){}
static void func2(){}
};
int Person::p_b = 0;
void test01()
{
Person p;
// Empty class memory space is: 1, C++ compiler will allocate 1 byte space, for distinguish the position of the objects in the memory space.
cout<<"size of p"<<sizeof(p)<<endl;
}
void test02()
{
Person p;
// One non-static member variables, mempory space is: 4, allocate memory according to the int
cout<<"size of p"<<sizeof(p)<<endl;
// One non-static member variables + One static mv, mempory space is: 4, as static mv is not belonging to the class
cout<<"size of p"<<sizeof(p)<<endl;
// One non-static member variables + One static mv + One member function, mempory space is: 4, as member funtion is not belonging to the class
cout<<"size of p"<<sizeof(p)<<endl;
// One non-static member variables + One static mv + One member function + One static member funtion, mempory space is: 4, as static member funtion is not belonging to the class
cout<<"size of p"<<sizeof(p)<<endl;
}
this pointer point to the object to which the called function belongs
Usage:
- When the formal parameters and member variable have the same name, we use 'this' to distinguish
- In the class, when the non-static member function return the object itself, we use return *this
class Person
{
public:
Person(int age)
{
// this pointer point to the object to which the called function belongs
this->age = age;
}
// withou &, it will return a value, which is a new object, but with &, it is not new object.
Person& PersonAddAge(Person &p)
{
this->age += p.age;
// this point to p2, *this is the p2 itself
return *this;
}
int age;
}
void test01()
{
Person p1(18);
cout<<p1.age<<endl;
}
void test02()
{
Person p1(10);
Person p2(10);
// Chain programming
p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
cout<<p2.age<<endl;
}
The empty pointer can call the member function as well, but we should also notice whether 'this' pointer is used or not
class Person
{
public:
void showClassName()
{
cout<<"This is Person Class"<<endl;
}
void showPersonAge()
{
// Wrong, since the imported pointer is NULL, to avoid that, we add a discriminant
if (this == NULL)
{
return;
}
cout<<"age="<<age<<endl;
}
int p_a;
}
void test01()
{
Person *p = NULL;
p->showClassName();
p->showPersonAge();
}
const function
-
member function + const decorate
-
We cannot modify member attribute in the const function
-
But is wen add mutable when disclaimer the attribute of the member, we can modify the attributes
class Person { public: // Once we add const, the void showPerson() const // this is equal to const Person *const this { // Once we add const, this statement is wrong, as 'this' pointer is a const pointer, the ditection cannot be chagned, but the value of the pointing can be changed. this->p_a = 100; this->p_b = 100; } void func(){}; int p_a; // But is we add mutable, we can modify the value in the const member function mutable int p_b; }
const object
-
disclaimer the object + const
-
const object can only call the const function
void test02() { const Person p; // add const change to const object p.p_a = 100; //wrong, we cannot change the value in const object p.p_b = 100; // correct, as p_b defined with mutable // const object can only call the const function p.showPerson(); p.func(); // wrong, const object cannot access the general member function, as general member function can change the attributes }
In the program, sometime we want to use a technique to achieve a special function or class to access the private attribute, this technique is named Friend.
Friend three achievements:
-
Global function as friend
class Builing { fridend void good_fri(Building *building); public: Building() { m_SittingRoom = "Living Room"; m_BedRoom = "Bed Room"; } public: string m_SittingRoom; private: string m_BedRoom; } // Global Function void good_fri(Building *building) { cout<<"GOOD friend visiting the:"<<building->m_SittingRoom<<endl; cout<<"GOOD friend visiting the:"<<building->m_BedRoom<<endl; } void test01() { Building building; good_fri(&building); }
-
Class as friend
class Builing { public: Building(); public: string m_SittingRoom; private: string m_BedRoom; }; // Function statement out of the class Building::Building() { m_SittingRoom = "Living Room"; m_BedRoom = "Bed Room"; } class Good_fri { public: Good_fri(); void visit(); // Visit functon to call the attributes of the Builidng class Building *building; }; Good_fri::Good_fri() { // Create building object m_building = new Building; } Good_fri::visit() { cout<<"Good friend are calling:"<<building->m_SittingRoom<<endl; cout<<"Good friend are calling:"<<building->m_BedRoom<<endl; } void test01() { Good_fri gg; gg.visit(); }
-
Member function as friend
class Building; class Good_fri { public: good_fri(); // Constructor - to initialize the status. void visit(); void visit2(); private: Building *building }; class Building { fridend void Good_gri::visit(); public: Building(); public: string m_SittingRoom; private: string m_BedRoom; } Building::Building() { this->m_SittingRoom = "Living Room"; this->m_BedRoom = "Bed Room"; } Good_fri::good_fri() { building = new Building; // Need to check the usage of 'new' } void Good_fri::visit() { cout<<"Good friend visiting:"<<building->m_SittingRoom<<endl; cout<<"Good friend visiting:"<<building->m_BedRoom<<endl; } void Good_fri::visit2() { cout<<"Good friend visiting:"<<building->m_SittingRoom<<endl; //cout<<"Good friend visiting:"<<building->m_BedRoom<<endl; } void test01() { Good_fri gg; gg.visit(); gg.visit2(); }
-
Using member function to overload +
class Person { public: // 1. Using member function to overload + Person operator+(Person &p) { Person temp; temp.m_A = this->m_A + p.m_A; temp.m_B = this->m_B + p.m_B; return temp; } int m_A; int m_B; }; // Function overload Person operator+(Person &p1, int num) { Person temp; temp.m_A = p1.m_A + num; temp.m_B = p1.m_B + num; return temp; } void test01() { Person p1; p1.m_A = 10; p1.m_B = 10; Person p2; p2.m_A = 10; p2.m_B = 10; Person p3 = p1 + p2; // 1. Person p3 = p1.operator+(p2) | 2. Person p3 = operator+(p1,p2) // Function overload Person p4 = p1 + 100; }
-
Using global function to overload +
Person operator+(Person &p1, Person &p2) { Person temp; temp.m_A = p1.m_A + p2.m_A; temp.m_B = p1.m_B + p2.m_B; return temp; }
Left Shifting Operator
Usage: output the self-defined type of data
class Person
{
friend: ostream & operator<<(ostream &cout, Person &p);
public:
Person(int a, int b)
{
m_A = a;
m_B = b;
}
private:
// Generally it is not recommend using Member functon to overload, p.operator<<(cout), in the result the cout would be in RHS.
int m_A;
int m_B;
};
// Using global function to overload left shifting operator
ostream & operator<<(ostream &cout, Person &p) // The reason for using citation is that it is not necessary to create a new parameters, only one parameter in this program
{
cout<<"m_A="<<p.m_A<<"m_B="<<p.m_B;
return cout
}
void test01()
{
Person p(10,10);
//p.m_A = 10;
//p.m_B = 10;
cout<<p<<endl; // chain programming logic
}
Usage: Achieve int data type by overload increment operator
class MyInteger
{
fridend: ostream & operator<<(ostream &cout, MyInteger myint);
public:
MyInteger(){
m_Num = 0;
}
// Overload Pre-increment
MyInteger & operator++() // The reason of using citation is to operatre on one target
{
// Plus first
m_NUm++;
// Then return the value
return *this;
}
// Overload Post-increment
MyInteger & operator++(int) // int is a Placeholder parameter for distinguishing the pre and post
{
// Record the result first
MyInteger temp = *this;
// Then plus 1
m_Num++
// Return the record result
return temp;
}
private:
int m_Num;
};
// Overload left shifting operator
ostream & operator<<(ostream &cout, MyInteger myint)
{
cout<<myint.m_Num;
return cout;
}
void test01()
{
MyInteger myint;
cout<<myint<<endl; // We need overload <<
}
int main()
{
test01()
}
C++ compiler will add four function at least for a class:
- Default constructor function (no parameters, empty statement)
- Default destructor function (no parameters, empty statement)
- Default copy constructor function to perform value copy
- Assign operator = to perform value copy
If the attribute in the class points to the heap are, then the assigning operation will face the deep and shallow copy problems
class Person
{
public:
// Constructor to initlize value
Person(int age)
{
// Create age data in heap area
m_Age = new int(age);
}
// Operator = overload
Person & operator=(Person &p)
{
// Cheack the pointer release
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
// However, the defalut '=' is shallow copy, if the user release the heap area data, it will be released twice can cause error. Thus, we need to provide deep copy
m_Age = p.m_Age;
// Deep copy, by using pointer for address description and use new to create in heap area.
m_Age = new int(*p.m_Age);
return *this; // We don't want to create new variable in terms of memory address
}
// Destructor to release the data in heap area
~Person()
{
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
}
// Age pointer (Why using pointer?)
int *m_Age;
};
// Target we want is to equalize two Person instance, thus we need overload the operator =
void test01()
{
Person p1(10);
Person p2;
p2 = p1;
// But if we want to use continous '='
Person p3;
p3 = p2 = p1; // We need to check the return data type
}
Usage: Compare two self-defined objects
class Person
{
public:
Person(string name, int age)
{
m_Nae = name;
m_Age = age;
}
// Overload '=='
void operator==(Person &p)
{
if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
{
return true;
}
return false;
}
// Overload '!='
void operator!=(Person &p)
{
if (this->m_Name != p.m_Name || this->m_Age != p.m_Age)
{
return true;
}
return false;
}
string m_Name;
int m_Age;
};
void test01()
{
Person p1("Tom", 18);
Person p2("Tom", 18);
if (p1 == p2)
{
cout<<"P1 = P2"<<endl;
}
else
{
cout<<"P1 not = P2"<<endl;
}
if (p1 != p2)
{
cout<<"P1 not = P2"<<endl;
}
}
Also called functors
class MyPrint
{
public:
// Function call operator overloading
void operator()(string test)
{
cout<<test<<endl;
}
};
void MyPrint02()
{
MyPring myPrint;
myPring("hello world"); // Functor
MyPring02("hello world");
}
void test01()
{
MyPring myPring;
myPrint("hello world");
}
class MyAdd
{
public:
int operator()(int num1, int num2)
{
return num1 + num2;
}
}
void test02()
{
MyAdd myadd;
int ret = myadd(100, 100);
cout<<"ret="<<ret<<endl;
// Anonymous object
cout<<MyAdd()(100,100)<<endl;
}
Usage: Decreasing the repeated code
Grammar: class sub_class : inheritance_approach Father_class
Sub_class also named as Derived class
Father class also named as Base class
Three approaches:
For quick understanding, we can regard that the inheritance approaches are describing the sub-class data type not from the base class approach.
-
Public inheritance
// Base class class Base1 { public: int m_A; protected: int m_B; private: int m_C; }; // Derived class class Son1:public Base1 { public: void func() { m_A = 10; // Public authority member can be accessed m_B = 10; // Protected authority member is sitll protected in derived class m_C = 10; // Wrong ! Proviate member cannot be accessed } } void test01() { Son1 s1; s1.m_A = 100; s1.m_B = 100; // Wrong! Protected can not be accessed when out of the class }
-
Protect inheritance
// Base class class Base2 { public: int m_A; protected: int m_B; private: int m_C; }; // Derived class class Son2:protected Base2 { public: void func() { m_A = 100; // Changed to protected in sub_class m_B = 100; // Changed to protected in sub_class m_C = 100; // Wrong! Private member cannot be accessed } }; void test02() { Son2 s2; s2.m_A = 100; // Wrong! In Son2, m_A changed to protected authority, thus cannot be accessed when out of the class }
-
Private inheritance
// Base class class Base3 { public: int m_A; protected: int m_B; private: int m_C; }; // Derived class class Son3:private Base3 { public: void func() { m_A = 100; // Changed to privated in sub_class m_B = 100; // Changed to privated in sub_class m_C = 100; // Wrong! Private member cannot be accessed } }; void test03() { Son3 s3; s3.m_A = 100; // Wrong, m_A changed to privated in Son3 }
The members inherited from the Base class, which of them belongs to sub_class objects?
// Base class
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son:public Base
{
public:
int m_D;
}
void test01()
{
cout<<"size of Son"<<sizeof(Son)<<endl; // The answer is 16, int(A+B+C+D)
/* Conclusion:
1. All the non-static member attribute will be inherited by Son class
2. The private member attribute is inherited as well, but it is hided, thus we cannot access*/
}
// Check the inheritance in terminal
By moving to the current route
c1 /d1 reportSingleClassLayoutSon "xxx.cpp"
After the Son class inherit Base class, when create object in Son class, it will call the structor function in Base class
class Base
{
public:
Base()
{
cout<<"Base constructor "<<end;''
}
~Base()
{
cout<<"Base destructor "<<endl;
}
};
class Son:public Base
{
public:
Son()
{
cout<<"Son constructor "<<end;''
}
~Son()
{
cout<<"Son destructor "<<endl;
}
};
void test01()
{
Son s1;
}
/* Results:
1. Base constructor
2. Son constructor
3. Son destructor
4. Base destructor
*/
If the Son class and the Base class has the member with the same name?
- Access the same name member in Son class, just do it
- Access the same name member in Base class, add the scope
class Base
{
public:
Base()
{
m_A = 100;
}
void func()
{
cout<<"Base-func() calling"<<endl;
}
// If, another function
void func(int a)
{
cout<<"Base-func(int a) calling"<<endl;
}
int m_A;
};
class Son:public Base
{
public:
Son()
{
m_A = 200;
}
void func()
{
cout<<"Son-func() calling"<<endl;
}
int m_A;
}
void test01()
{
Son s1;
cout<<s1.m_A<<endl; // Just do it
cout<<s1.Base::m_A<<endl;
}
void test02()
{
Son s2;
s2.func(); // Just do it - Son class function
s2.Base::func();
s2.func(100); // Wrong! If the member function in Son class has the same name with its Base class, that member function will hite all the function with the same name in the Base class
s2.Bash::func(100); // Correct
}
If the static member has the same name with the non-static member:
- Access the same name member in Son class, just do it
- Access the same name member in Base class, add the scope
class Base
{
public:
static int m_A;
static void func();
};
int Base::m_A = 100;
void Base::func()
{
cout<<"Base - func"<<endl;
}
class Son:public Base
{
public:
static int m_A;
static void func();
};
int Son::m_A = 200;
void Son::func()
{
cout<<"Son - func"<<endl;
}
// Static member attribute with the same name
void test01()
{
// Access by object
Son s;
cout<<"Son m_A"<<s.m_A<<endl;
cout<<"Base m_A"<<s.Base::m_A<<endl;
// Access by class name
cout<<"Son m_A"<<Son::m_A<<endl;
cout<<"Base m_A"<<Son::Base::m_A<<endl;
// The first colon is accessing by class name, the 2nd colon is the scope of the Base class
}
void test02()
{
// Access by object
Son s;
s.func();
s.Base::func();
// Access by class name
Son::func();
Son::Base::func();
// If the static member function in Son class has the same name with its Base class, that member function will hite all the function with the same name in the Base class
}
class Son_class : Inherit_approach Base 1, Inherit_approach Base 2
Multi-inheritance can cause same name members, we need to add scope to distinguish
IT IS NOT RECOMMENDED !!!
class Base1
{
public:
int m_A;
};
class Base2
{
public:
int m_A;
};
class Son::public Base1, public Base2
{
}
Concepts:
- Two derived class inherit the same Base class
- A new class inherit those two derived class
class Animal
{
public:
int m_Age;
};
// Adopt virtual inheritance can solve it
class Sheep:virtual public Animal
{
};
class Camel:virtual public Animal
{
};
class Alpaca:public Sheep, public Camel
{
};
void test01()
{
Alpaca a;
a.Sheep::m_Age = 10;
a.Alpaca::m_Age = 10;
a.m_Age;
// But only one piece of data is enough
}
Two types of polymorphism:
- Static polymorphism: function overload and operator overload are static polymorphism, function name reuse
- Dynamic polymorphism: Derived class and virtual function
The difference between the static and dynamic polymorphism:
- Function address in static polymorphism binding early - determine the function address when compiling
- Function address in dynamic polymorphism binding later - determine the function address when running
Polymorphism conditions:
- Inheritance relationship
- Derived class re-write the virtual function in Base class
How to use:
- Using the pointer or citation in Base class to execute Derived class
class Animal
{
public:
// If we add virtual, it changed to dynamic polymorphism, the function addree is binding later
virtual void speak()
{
cout<<"Animal speaking"<<endl;
}
};
class Cat:public Animal
{
public:
void speak()
{
cout<<"Miao"<<endl;
}
};
// Function for speaking
void doSpeak(Animal &animal)
{
animal.speak();
}
void test01()
{
Cat cat;
doSpeak(cat); // The result is "Animal speaking", because the is static polymorphism, the function address is binding early.
}
class Animal
{
public:
virtual void speak()
{
cout<<"Animal speaking"<<endl;
}
}
void test01()
{
cout<<sizeof(Animal)<<endl; // The result will be 1
cout<<sizeof(Animal)<<endl; // But if we add virtual function, the results will be 4, because it includes a vfptr, and this pointer will point to a vftable, this vftable include the address of the function, thus it is 4
};
The inner structure of Animal
vfptr: virtual function pointer
In polymorphism, generally the statement of the virtual function in Base class is meaningless, it is more important in Derived class
Therefore we can change the virtual function to Pure virtual function
Grammar: virtual return_type function_name (parameter list) = 0;
If a class has the Pure virtual function, this class is named abstract class
The characteristics of the Abstract class:
- Can not be instance of object
- The derived class must re-write the Pure virtual function, otherwise it is Abstract class as well.
class Base
{
public:
virtual void func() = 0;
};
class Derived:public Base
{
public:
virtual void func()
{
cout<<"Func calling"<<endl;
};
};
void test01()
{
Base *base = new Derived;
base->func();
}
In polymorphism, if the attribute of the Derived class is created in heap area, when the pointer is released it cannot tough the Donstructor code in Derived class
Solutions: Change the constructor function in Base class as virtual Donstructor or pure vd
Identical points between VD and PVD:
- Solve the pointer in Base class to release the objects in Derived class
- Need detailed statement in functions
Difference:
- If it is PVD, this class is Abstract class, cannot create instance object
class Animal
{
public:
Animal()
{
cout<<"Animal constructor calling"<<endl;
}
// The pointer in Base class cannot tough the code in Derived class, thus we need VC
virtual ~Animal() = 0;
};
Animal::~Animal()
{
cout<,"Animal destructor calling"<<endl;
}
class Cat:public Animal
{
public:
Cat(string name)
{
cout<<"Cat constructor calling"<<endl;
m_Name = new string(name);
}
virtual void Speak()
{
cout<<*m_Name<<"Miao"<<endl;
}
~Cat()
{
cout<<"Cat destrucotr calling"<<endl;
if (this->m_NAME != Null)
{
delete m_Name;
m_Name = NULL;
}
}
string *m_Name; // Create data in heap area
}
void test01()
{
Animal *animal = new Cat("Tom");
animal -> Speak();
delete animal;
}
happy