OOP Summary

OOP

目录

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 list
  • envp : 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 (引用).

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 ::

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;
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<> and std::shared_ptr<> instead of new and delete.

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

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 not
  • constexpr : 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 namespace
    • using 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 modifications
      • void 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

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.

posted @ 2021-06-05 20:41  zjp_shadow  阅读(182)  评论(0编辑  收藏  举报