OOP Summary
OOP
- OOP
- Preliminaries
- Class
- Functions and Variables
Preliminaries
Linux Basics
- Change Password:
passwd
- shutdown:
sudo shutdown -h 3
(broadcast to all users in 3 mins) - reboot:
sudo reboot -r now
- create an empty file:
touch file-name
- Display file contents:
cat/more/less/head/tail file-name
- find name of current directory:
pwd
Command-line Arguments
Access argv and envp in the main routine
int main(int argc, char **argv, char **envp)
argc
: Number of arguments in the listenvp
: Shell reads in environment variables into envp list; can be omitted if not needed
Compilation and multiple-file projects
options for g++
-o filename
: specify the output filename-E
: Stop after the preprocessing stage (只进行预编译)-S
: Stop before assembling (生成汇编代码,不进行汇编)-c
: do not run the linker (不进行链接)
Declaration
A function (函数) or variable (变量) can be defined only once, but can be declared many times.
- Variable declaration:
extern int x;
- Function declaration:
extern void sum (int argc, char* argv[]);
Include Header Files
-
Preprocessor directive (预编译命令)
#include
- Angle brackets
<>
: typically for system headers - Double quotes
" "
: First search in current directory where the compilation is launched
Suggestion: include related header files in all the source files, wherever the declared functions are either defined or referenced (引用).
- Angle brackets
Makefile
Separate Compilation
-
Different source files (.cpp) (源文件) are compiled separately into different relocatable object files(.o or obj) (可重定位的目标文件)
g++ -c main.cpp -o main.o
-
The relocatable object files are linked into the executable object file (可执行目标文件) by a tool called linker (链接器)
g++ main.o sum.o product.o -o test.exe
make and Makefile
A tool called make is introduced to manage the compilation process.
- Dependency rule (依赖规则)
- Defined by comparing the modification dates of the source/header files (比较文件的更新日期)
- When a dependency rule is satisfied, the corresponding (compilation/linking) commands will be executed.
test.exe: product.cpp sum.cpp main.cpp functions.h
g++ product.cpp sum.cpp main.cpp -o test.exe
Dependency rule: if test.exe does not exist or is older than any of the files after the colon(😃, the following command (g++ …) will be executed.
an example:
test.exe: product.o sum.o main.o
g++ product.o sum.o main.o -o test.exe
product.o: product.cpp functions.h
g++ -c product.cpp -o product.o
sum.o: sum.cpp functions.h
g++ -c sum.cpp -o sum.o
main.o: main.cpp functions.h
g++ -c main.cpp -o main.o
More on Makefile
- Default target is all
- If one specific target is needed:
make test1.exe
There are tools for automatically generating the makefile, such as cmake.
Library
Static library (Linux)
A particular file of archive format (with .a suffix), which is a collection of concatenated relocatable object files, with a header describing the size and location of each object file.
g++ -static main.o test.a -o test.out
g++ -static main.o -L. -ltest -o test.out
-static: tells the linker to build a fully linked executable object file without any further linking at load time
Shared Library
An object module (with .so suffix) loaded at an arbitrary memory address (内存地址) and linked with a program in memory, at either run time (运行期) or load time (启动期). This is called dynamic linking (动态链接)
g++ -c -fPIC sum.cxx -o sum.o
g++ -shared -o libtest.so product.o sum.o
Class
variables and function are encapsulated (封装) into a structure called class.
Key features of OOP:
- Abstraction (抽象)
- Encapsulation (封装) (information hiding)
- Inheritance (继承)
- Polymorphism (多态)
Object & Abstraction
Member Variable and Member Function
class Bird{
public:
void fly();
void sing();
void eat(); //Member Functions
private:
std::string name_;
std::string color_;
float weight_;
float height_; //Member Variables
};
Access Control to Class Members
-
Do not restrict class designers (设计者)
-
Restrict class users (使用者)
Example:
ClassName objectName; objectName.memberVariable = XX; //should be restricted objectName.memberFunction(YY); //without restrict
Public Members
- accessible from objects by class users
- the interface (接口) between class designers and the class users
- When no keyword is given
- Default access control for class is private
- Default access control for struct is public
Private Members
- only accessible within member functions by class designers
- This enables the information hiding (信息隐藏) for the class designers
Protected Members
- Similar access control to
private
- Different from
private
for the derived classes- Used in inheritance (继承)
Definition of Member Functions
Member function
- Declared inside the class definition (类声明内部)
- Typically defined outside the class definition
- With scope resolution operator
::
- With scope resolution operator
Inline function
Functions less than 10 lines can be declared inline to improve efficiency
#ifndef BIRD_H_
#define BIRD_H_ //Preprocessor directive
#include <iostream>
#include <string>
class Bird {
public:
void setSpeed(float speed) {
speed_ = speed;
}//inline functions
private:
float speed_;
};
#endif
#include "bird.h"
using namespace std;
void Bird::fly(float time) {
float distance = speed_ * time;
cout << name_ << " flies distance: " << distance << endl;
}
Encapsulation
-
Integrate attribute and behavior into an entity
-
Improve safety
- Set different access control (访问权限控制) for different members.
- Class users can only access public members (also called interfaces, 接口)
-
Decouple (解耦) between class users and designers
this Pointer
-
Keyword
this
: points to the address(地址) of the current object. -
Usage
Return current object’s address from its member functions
- Typically used in operator overloading(e.g.,
=
):return *this;
- Typically used in operator overloading(e.g.,
class Bird {
public:
void fly(float time);
Bird* setName(char *name) {
name_ = name;
return this;
}
private:
std::string name_;
};
//User program:
Bird b;
b.setName(“Eagle")‐>fly(10.0);
Constructor (构造函数)
Lifetime (生命期) or scope (作用域) of objects
Constructor (ctor, 构造函数) and destructor (dtor, 析构函数) are introduced for better SAFETY (安全性)
-
Special member functions for initialization (初始化) and cleanup (清除).
-
Automatically called by the compiler (编译器) when an object is being created (创建) or deleted (销毁)
-
Constructors and destructor are typically
public
call constructor/destructor by
new/delete
Why constructor is necessary?
Constructor provides a place to ensure certain task be executed, such as member initialization, along with object creation (对象创建时进行初始化)
Avoids the careless/wrong usage of a class
Constructor is automatically called by the compiler when the object is being created for definition of an object:
ClassA a; //Stack object
ClassA *pA = new ClassA; //Heap object
-
Constructors have the same name as the class
-
Parameters can be provided in constructor for initialization
class Tree { int height_; public: Tree() { height_ = 0; } // Constructor 1 Tree(int h) { height_ = h; } // Constructor 2 };
Default Constructor
a special constructor without function arguments (above. Constructor 1)
When no constructor is defined in a class, the compiler will synthesize (合成) a default constructor, called a default default constructor
Using default keyword for Default Constructor
The use of the default
keyword is preferred in modern C++ code
class Tree {
int height_{0};
public:
Tree() = default; //equals to Tree(){}
};
Member Initializer List
class Tree {
int height_ {0}, year_ {0};
public:
Tree(int h, int y) : year_{y}, height_{h} { }
};
enjoys better efficiency
Keyword explicit
Single-parameter constructor enables the implicit type conversion (隐式类型转换)
To forbid such conversions, use keyword explicit
.
class Tree {
int height_{0};
public:
Tree(int h):height_{h} {}
//explicit Tree(int h):height_{h} {}
}; //…
void foo(Tree t){
//…
}
foo(5); //works if no explicit keyword, error if explicit is used
Delegating Constructor
Depends on other constructors for the initialization
class Tree {
int height_, size_;
public:
Tree(int h) : height_{h} { }
Tree() : Tree {h} { size_ = 0; }//Delegating constructor
};
Destructor (析构函数)
Clean up the resource (memory) occupied by the object. A special function called destructor(prepare-to-die) is introduced for cleanup.
Destructor is automatically called by compiler when:
- An object goes out of its scope
- Using
delete
:ClassA *pA = new ClassA; delete pA;
- In modern C++, we can use
std::unique_ptr<>
andstd::shared_ptr<>
instead ofnew
anddelete
.
DON’T explicitly call (显式调用) the destructor unless necessary.
pA->~ClassA(); // works but NOT recommended
- Destructor
- Name of destructor is the same as the class, preceded by
~
- No function parameters
- There should be only one destructor
- The destructor needs to be public to be automatically called
- Name of destructor is the same as the class, preceded by
When is a destructor necessary?
- To free (释放) member variables allocated on the heap (堆)
- Free other occupied resources (socket, printer, etc.)
Default Keyword
the same as previous
Stack vs. Heap Variables
-
Stack (栈) and Heap (堆) are memory (内存) fragments for storing the variables of the program.
-
Stack
- Limited size of several kilobytes (千字节) to megabytes (兆) depending on the system (系统) and compiler settings.
- Store local variables of different functions
- Example of variables on stack:
int i; double val; ClassA a;
- Stack overflow (栈溢出)may occur for large array variables (e.g.,
int a[100000000];
) and deep recursive functions (递归函数)
-
Heap
-
The size of heap is much larger than stack: so large array variables(数组变量)should be defined on heap
-
How to define variables on the heap?
int num = 1000000; // on stack int *arr = new int[num]; delete []arr; // on heap int *arr = (int*)malloc(sizeof(int)*num); free(arr); // on heap
-
Functions and Variables
Macro
Macro is an improvement over magic numbers based on preprocessor directive (预编译命令).
- ``#ifdef` checks whether a macro is defined
Useful macros in debugging
__func__
: function name in const character array__FILE__
: file name in string literal (字面值)__LINE__
: current line number in integer literal__TIME__
: time of the compilation in string literal__DATE__
: date of the compilation in string literal
Inclusion Guard for Headers
#define XX
: defines a macro XX#ifndef XX
: if XX is not defined, go forward; otherwise,
skip the following part before #endif
Header format
#ifndef XX
#define XX
//body
#endif
Without the header guard, a header file may be included multiple times in one source file, causing compiling errors.
Undefine Macros
#undef VALUE
helpful in avoiding name conflicts.
assert and NDEBUG
assert
: is a preprocessor macro for debugging
-
assert(expr);
If expr is false, the program will be terminated with error messages.
-
Header file:
#include <cassert>
When the program is to be released, assert should be disabled by macro NDEBUG
.
- Way No. 1: “``#define NDEBUG`” in source file
- Way No. 2:
g++ -D NDEBUG xxx.cpp
Prefer enum/const to Macros (宏)
Design principle: avoid macros as much as possible.
Prefer compiler (编译器) to preprocessor (预编译器)
Replace function-like macros by inline functions (内联函数).
Const
const
(常量): used to define read-only (只读) variables.
const int buf_size {100};
char buf[buf_size];
const
defaults to internal linkage (内部链接)
-
Visible (可见) only within the source file where it is defined
-
Invisible (不可见) in other translation units (翻译单元) (.cpp files)
-
Definition (定义) and initialization (初始化) must be done at the same time.
Enum Instead of Macros
enum
: all values are available during compilation
- Restriction (局限): can only represent integers
class Bunch {
enum { size = 1000 };
int arr[size];
};
Using const in Class
-
Constructor Initializer List
class fred{ const int size; public: fred(); }; fred::fred() : size{100}{ // constructor body }
Const objects can only call const member functions.
int main(void) {
A obj; obj.show(); obj.add(3);
const A var{3}; var.show(); // which is called?
var.add(4); // Error: const objects cannot call non‐const members
return 0;
}
as_const and const_cast
Conversion between const and non-const (常量与非常量之间的转换)
const_cast
: cast between const and non-const
const_cast<Type*>(expression)
const_cast<Type&>(expression)
as_const
: cast non-const into const
Top-Level vs. Low-Level Const
ClassA a, b;
ClassA * const pA = &a;
pA‐>a_ = 3; //error
pA = &b;
ClassA a, b;
const ClassA * pA = &a;
pA‐>a_ = 3; //error
pA = &b;
Function Overloading
Function overloading (函数重载): allow the same function name to be used with different arguments
Reuse original compiler: transform overloaded functions into general functions.
-
For the function arguments:
f(int)
\(\to\)f_int()
f(float)
\(\to\)f_float()
-
For the scope (作用域):
X::f()
\(\to\)_X_f()
not return type : The compiler cannot determine which function is called
Grammars on Function Overloading
- A const parameter is only distinguished from a non-const parameter for references and pointers.
- For a fundamental type such as int, const int is identical to int.
Default Arguments
Default argument (缺省参数): function argument in function declaration (函数声明) with default values(缺省值) that the compiler automaticallyassigns if not provided in the function call (函数调用)
Simulation(int a, int b = 0);
Grammar
-
Only tailing arguments can be defaulted
-
Default arguments are only given in function declaration(函数声明), NOT in function definition (函数定义)
- Exception: when there is no function declaration
//function declaration goes with function definition void func(int var, int var2 = 3) { ... }
-
Coding style: commented values can be given in function definition for better readability
void func(int var, int var2 = 3); //declaration …… void func(int var, int var2 /* = 3 */ ) { ... } //definition
Function Overloading vs. Default Arguments
Difference
- Overloaded functions: multiple function bodies (函数体)
- Default arguments: only one function body
Constant Expression
Constexpr functions
- Suggests the compiler to compute constant value from the function at compile time (编译期)
- Implicitly set as inline functions (缺省设为内联函数)
Constexpr expressions
- Requires the compiler to return constant value at compile time
Difference between const and constexpr expressions
const
: only requires that variables be constant (read-only), no matter it is constant since compilation or notconstexpr
: the variable should be constant since compilation
#include <iostream>
#include <array>
using namespace std;
constexpr int arrsize(int i) {
return i*i; // a single return statement
}
int main() {
const int i {10};
std::array<int, arrsize(i)> arr = {0};
int arr2[arrsize(10)] = {0};
for (int& x : arr) { std::cout << ' ' << x; }
std::cout << std::endl;
for (int& x : arr2) { std::cout << ' ' << x; }
constexpr int a = 5;
const int b = 2;
int c = 0;
constexpr int d = a+b; //Ok
a=3; //Error: a is const
constexpr int e = a+c; //Error: c is not constant
}
Inline Function
Beside general inline functions, there is a special inline function: Inline member functions (内联成员函数)
-
Grammar:
inline Type foo (Type1 a, Type2 b) { ... }
-
The
inline
keyword is only sending a request to the compiler (向编译器发送内联请求), which may be ignored.
Inline Member Functions
A member function with its body (函数体) defined within the class definition is automatically inline. On modern processors, smaller code usually runs faster due to better use of the instruction cache.
class A {
int x_;
public:
//accessor (访问器) and mutator (修改器) are both automatically inline because they are defined within the class definition, i.e., the function bodies are defined here.
int setX(int x) { x_ = x; }
int getX() { return x_; }
};
Namespace
namespace
: a name wrapper to enclose the defined names in a distinct space.
- Typically for avoiding name collisions (命名冲突)of different global functions, global variables, and classes
But namespace
is dedicated to (专用于) the creation of a new space.
namespace U{
void f() {}
class A {};
}//;
int main() {
U::f();
using U::A;
A a;
return 0;
}
- Two ways of referring to names:
using namespace spaceName
:opens the whole namespaceusing spaceName::myName
:only opens myName
NEVER open namespaces by using in header files, which increases the chance of name collision (名字冲突)
Static Variable and Function(静态变量与静态函数)
Static (静态) data/variable: lives throughout lifetime of program
- Allocated once (分配存储空间) at a fixed address in a special static data area (静态存储区), which is neither stack (栈) nor heap (堆)
#include <iostream>
using namespace std;
void func() {
static int called_times {0}; // any difference from global variable?
++ called_times;
cout << "Called " << called_times << " times" << endl;
}
int main() {
for (int i {0}; i < 10; ++i) func();
}
Keyword static vs. extern
-
static
:Internal linkage: static variables/objects and static general functions (普通函数) are local to its translation unit (编译单元)
-
extern
:External linkage: visible to the linker (链接器) everywhere, i.e., external to the translation unit (编译单元) where it is defined
Example:
In A.cpp: int var = 1000; //define a global variable In B.cpp: extern int var; //refer to var w/o header file
Static Class Members
Static members belong to the class: they are shared by all objects of the same class
-
Static member functions:
Cannot call non-static member functions or access non-static member variables
-
Two ways of creating static arrays:
enum
static const
class A { static int i; //static needed static const int size {200}; enum { size2 = 100, size3 = 200 }; int array[size]; int array2[size2]; int array3[size3]; public: static void foo(); //static needed // ... }; // source file (.cpp) int A::i {1234};//static not needed void ClassA::foo(){//static not needed //do something }
Singleton
-
Singleton design pattern
- Ensures only one instance (object) for a given class
- By setting constructors as private
#include <iostream> using namespace std; class Egg { static Egg e; int i; Egg(int ii) : i(ii) {} public: static Egg* instance() { return &e; } int val() const { return i; } }; Egg Egg::e(47); int main() { // Egg x(1); // Error: can't create an Egg // You can access the single instance: cout << Egg::instance()‐>val() << endl; //However, this works! Egg e = *Egg::instance(); }
Reference
Reference (引用) : alias (别名) of a given object or variable
-
All operations (操作) on a reference affects the referenced object
int iVal = 1000; int &refVal{iVal}; refVal = 1024; //changes iVal
-
Reference: A const pointer (指针常量) that is automatically dereferenced
-
NULL references are not allowed
-
But NULL pointers are allowed
int *pVal{nullptr}; //OK
-
Reference Usage
-
References are mainly used for function parameters (函数形参) and return value
-
Modify values or carry return values by function arguments (函数实参)
swap(a, b); //void swap(int &a, int &b);
string& larger(string& s1, string& s2){ return s1 > s2? s1 : s2; // Return a reference to the larger string }
-
Avoid making copies of function arguments and/or return value for better efficiency
void foo(ARR &a, ARR &b)
-
Design principle: Least privilege principle (最小特权原则)
Award function enough access to accomplish the task, but no more
void foo(const ARR &a, const ARR &b)
-
-
In a range-based for loop(pass by reference)
double temperatures[] {45.5, 50.0, 48.2, 57.0, 63.8}; for (auto &t : temperatures){ ... // can use t to modify the value of an element }
Copy constructor (拷贝构造函数)
A special constructor for cloning a new object from an existing object. Both objects are of the same class
Grammar: X(const X& obj) { ... }
- Argument takes an existing object
Examples of constructor definition
Box::Box(int l, int w, int h) : length {box.length},
width {box.width}, height {box.height} {}
Box::Box(const Box& box) : Box{box.length, box.width, box.height} {} //delegating constructor
Copy constructor is called when:
A a; // default constructor
A b{a}; // calls copy constructor
A c = a; // calls copy constructor
void swap(A a, A b); // pass arguments by copy constructor
A f(A x); // both arguments and return value passed by copy constructor
If the designer does NOT define a copy constructor,the compiler will synthesize (合
成) one automatically
- Bitcopy (位拷贝) is used in the synthesized copy constructor
- This is error-prone for classes with pointer members
Avoid Copy Constructor
- Pass and return objects by reference
complex& func(complex& c) { return c; }
- For better safety, use
const
to avoid modificationsvoid func2(const complex& c) { … }
Keyword delete
delete
: Disable certain member function without defining the function body
class A{
public:
A(int a) {};
A(double) = delete; // conversion disabled
A(const A&) = delete; // copy ctor disabled
A& operator=(const A&) = delete;// assignment disabled
};
A a{10}; // OK
A b{3.14}; // Error: conversion from double to int disabled
A c{a}; // Error: copy constructor disabled
a = b; // Error: assignment operator disabled
Move Constructor
lvalue (左值) and rvalue (右值)
-
lvalue: left-hand value of
=
- Its memory supports both read and write
int *p, i; // then *p and ++i are lvalues
-
rvalue: right-hand value of
=
-
Its memory is read only, including literals (字面量, e.g., 10, “Hello World”) and temporaries (临时对象)
-
int *p, i; //&p and i++ are rvalues int f() {...} //f() is rvalue
-
Const and Non-Const References
int f() {...} //f() is rvalue
const int& const_ref_lval = lval; // ok for lvalue
const int& const_ref_rval = 5; // ok for rvalue
const int& const_ref_rval = f(); // ok for rvalue
Rvalue Reference
An rvalue reference only binds to an rvalue, which is going to be destroyed.
Keyword: &&
int lval = 2; // lval is an lvalue
int f() {...} //f() is rvalue
int&& ref_rval = 5; // OK: binds to an rvalue 5
int&& ref_rval2 = lval; // Error: rvalue ref cannot bind to lvalue
int&& ref_rval3 = lval+5; // OK: binds to an rvalue
int&& ref_rval4 = f(); // OK: binds to an rvalue
When is rvalue reference useful?
template<class T>
void swap(T& a, T& b) {
T tmp(a); // now we have two copies of a
a = b; // now we have two copies of b
b = tmp; // now we have two copies of tmp
}
Actually, we do not need to make copies at all
- Grammar:
X(X&& obj)
- Object obj should be non-const
- Use std::move(x) to invoke the move constructor
- Here,
move
means that “you can treat x as an rvalue”, i.e., it may be destroyed
- Here,
Note: If a copy constructor or a destructor is defined, the compiler will NOT synthesize (合成) the move constructor
A(A&& a):m_size{a.m_size} {
this‐>m_arr = a.m_arr;
a.m_arr = nullptr;
}
template<class T>
void swap(T& a, T& b) {
T tmp = move(a); // could invalidate a, move constructor
a = move(b);// could invalidate b, move assignment
b = move(tmp);// could invalidate tmp, move assignment
}
Move assignment (移动赋值) is also used in the above example, which will be introduced when we learn operator overloading.