OOP in C++

OOP in C++

Object Oriented Programing in C++

new & delete

1
2
3
4
int *a = new int;
int *b = new int[10];
delete a;
delete [] b;

const modifier & pointers

const u v: u is const

a b const c d: b is const

1
2
3
4
5
6
const int a; // a is const
const int *b; // b points to a const variable
int * const c; // c is a const pointer to an int variable.
int const * const d; // d is a const pointer to a const variable.
int ** const; // a const pointer to a pointer to int

function overload

there can't be two functions with exactly same arguments.

conflicting definition: int f(int a, int b); void f(int x, int y)

Example1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int increase(int a, int delta) {
return a + delta;
}
int increase(int a, int& delta) {
return a + delta;
}
int main() {
int x = 4;
increase(x, 4);
// the first increase will be called because literal 4 can't be &
increase(x, x);
// compiler reported an error because it can't determine which function to call
return 0;
}

Example2:

1
2
3
4
5
6
7
8
9
10
11
int increase(int a, int& delta) {
return a + delta;
}
int increase(int a, const int& delta) {
return a + delta;
}
int main() {
int x = 4;
increase(x, x); // call the first one, because x is not const
increase(x, 4); // call the second one, because literal cannot be &.
}

Example3:

1
2
3
4
5
6
7
8
9
int f(int a) {
return a;
}
int f(double b) {
return ceil(b);
}
int main() {
f(4); // error
}

default args

the last few arguments of a function can have default values.

1
2
3
int f(int a, int b = 1, int c = 2) {
return a * b + c;
}

note: this will cause overloading confict mentioned above.

Class

Access Permissions:

1
2
3
4
5
class Class {
private:
public:
protected:
};

Default Permission:

  1. class: private
  2. struct: public

Static Member

1
2
3
4
5
6
struct Point {
static int member;
static double function() {
member = 0;
}
};

Static Member: Point::member, static member doesn't belong to any object.

Static Member Function: Can only access static member and function args.

const modifer

1
2
3
4
5
struct Class {
void func() const { // const on (*this)
;
}
}

Constructor

1
2
3
4
5
6
7
8
struct Class {
int x, y;
Class() = default;
// delete this line and there won't be a constructor that takes 0 arg
// Class() = delete;
Class(int x, int y) : x(x), y(y) { }
// x(x) means member x is constructed by arg x
};

Usually constructors are public, otherwise you can't construct outside.

Private constructor & Singleton Design Pattern

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Singleton
{
private:
Singleton() {}
static Singleton* instance = NULL;
public:
static Singleton* getInstance() {
// only one Singleton can be constructed
if (instance == NULL)
instance = new Singleton();
return instance;
}
};

copy constructor

Copy Constructor: A constructor that takes an argument, i.e. an instance of its type.

default copy constructor copies all members.

1
2
3
4
5
6
7
8
9
10
11
12
13
struct MyString {
char *arr;
MyString(){ arr = new char [100]; }
// you can delete const modifier
// but cannot delete & modifer, because it function arg without & will call a copy.
MyString(const MyString &rhs) : String() {
// default copy constructor will copy arr address
// this constructor will new an char array and copy its value.
int i;
for(i = 0; rhs.arr[i]; ++i) arr[i] = rhs.arr[i];
arr[i] = 0;
}
}

cast constructor

Cast Constructor: a constructor that takes an argument, i.e. an instance of other type

1
2
3
4
5
struct Type {
int x, y;
Type(int x) : x(x), y(0) { }
};
Type t = 1; // this will only call Type(1)

Deconstructor

default deconstructor does nothing.

1
2
3
4
5
6
7
8
struct Type {
int *arr;
Type() { arr = new int[10]; }
~Type() {
printf("Deconstructing\n");
delete [] arr;
}
};

Member Declaration & Implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class GeoDataHandler {
// declarations
private:
Point arr[1001]; int len; int freq[100][100];
bool isin(Point p, int x1, int y1, int x2, int y2);
public:
GeoDataHandler();
void append(int x, int y);
int query(int x1, int y1, int x2, int y2);
Point HH();
};
// implementations
void GeoDataHandler::append(int x, int y) {
arr[len++] = Point(x, y);
}
int GeoDataHandler::query(int x1, int y1, int x2, int y2) {
int counter = 0;
for (int i = 0; i < len; i++) {
if (arr[i].x >= x1 && arr[i].x <= x2 && arr[i].y >= y1 && arr[i].y <= y2) {
counter++;
freq[arr[i].x][arr[i].y]++;
}
}
return counter;
}
Point GeoDataHandler::HH() {
int max = -1; Point res;
for (int i = 0; i < 100; i++)
for (int j = 0; j < 100; j++)
if (max < freq[i][j]) {max = freq[i][j]; res = Point(i, j);}
return res;
}

An declared but not implemented function will not cause compile error,

and sometimes causes error when linking.

mutable modifier

a mutable member can be modified when this object is const.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class GeoDataHandler {
private:
Point arr[1001];
int len;
mutable int freq[100][100];
public:
int query(int x1, int y1, int x2, int y2) const { // const *this
int counter = 0;
for (int i = 0; i < len; i++) {
if (arr[i].x >= x1 && arr[i].x <= x2 && arr[i].y >= y1 && arr[i].y <= y2) {
counter++;
freq[arr[i].x][arr[i].y]++; // modify mutable member!
}
}
return counter;
}
};

struct Pair {
int x;
mutable int y;
// a key with a mutable value
bool operator < (const Pair __) const {
return x < __.x;
}
};
std::set <Pair> st;
st.insert((Pair){0, 1});
auto it = st.begin(); // returns a const iterator.
it -> y++; // ok
it -> x++; // can not modify x

friend

friend given a class/function the same access to this class.

1
2
3
4
5
6
class A{
private:
int x, y;
friend class B; // B's member can access A's private member x, y
friend void C::func(); // C's member function func can access A
};

overloading operators

a + b:

  1. as member: Type Type :: operator + (Type b);, where this points to a.
  2. as normal function: Type operator + (Type a, Type b);

operator as member cannot be declared outside class declaration.

1
2
3
4
5
6
7
8
9
10
11
12
class Type {
int x;
public:
Type(int x): x(x) { }
Type operator + (const Type rhs) const {
return x + rhs.x;
}
// x is private, should be declared as friend
friend Type operator * (Type a, Type b) const {
return a.x + b.x;
}
};

Special operators

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Type {
int x;
public:
Type& operator = (const doule &y) const {
x = y;
return *this;
}
// ++a
Type& operator ++ () const {
x++;
return *this;
}
// a++
Type operator ++ (int) const {
Type tmp = *this;
x++;
return tmp;
}
};
Type a;
double b;
a = b = 0.1;

Functor

Functor is a object that has a member operator ()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct PointComparator {
bool operator()(const Point& p, const Point& q) const {
if (p.x != q.x) return p.x < q.x;
return p.y < q.y;
}
};
int main() {
Point x(5, 4), y(1, 1), u(0, 0);
Point arr[3] = {x, y, u};
std::sort(arr, arr + 3, PointComparator());
/*
auto
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class PointComparatorDist {
Point o;
public:
PointComparatorDist(const Point& p) : o(p) { }
bool operator()(const Point& p, const Point& q) {
return p.distsq(o) < q.distsq(o);
}
}
int main()
{
Point x(5, 4), y(1, 1), u(0, 0);
Point arr[3] = {x, y, u};
std::sort(arr, arr + 3, PointComparatorDist(Point(0, 1)));
for (int i = 0; i < 3; i++) {
cout << arr[i].x << " " << arr[i].y << endl;
}
return 0;
}

type cast operator

1
2
3
4
5
6
7
8
9
10
11
struct A{
int x, y;
// int(a)
operator int() const {
return x + y;
}
// B(a)
operator B() const {
/* ... */;
}
};

右值引用

Type&& a,优先匹配右值并且引用它(如果没有右值对应的函数就去找左值)。

由于右值通常是计算过程中的临时量,有时可以取走对方的所有权。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct String {
char* arr; int len = 0;
String(char* str) {
arr = new char[10000];
strcpy(arr, str);
len = strlen(str);
}
String(String&& s) {
// move semantics
cout << "rvalue ref" << endl;
len = s.len;
arr = s.arr;
s.arr = NULL;
}
String& operator=(String&& s) {
cout << "rvalue assign" << endl;
len = s.len;
arr = s.arr;
s.arr = NULL;
return *this;
}
} };

std::move(x) 返回 \(x\) 的右值引用。

Inheritance

1
2
3
struct B: public A {
;
};

Permissions

  • Parent类中的 private 成员都对 Child 完全不可⻅
  • Parent类中除 private 成员都对 Child 可⻅,但具体访问权限如下:
    1. public 继承: 父类成员全部保持原访问权限
    2. private 继承: 父类成员全部访问权限改为 private
    3. protected 继承: 父类成员全部访问权限改为 protected

protected成员:

仅在

  1. 当前类内部
  2. 子类内部
  3. 友元

可以访问

Constructor

需要手动调用父类的构造函数。

Deconstructor

父类会在子类之后析构。

override

子类中相同的成员会覆盖父类。

  1. 同变量名,覆盖(不论类型)。
  2. 同函数名且同参数,覆盖。

被覆盖后,父类的成员依然存在,只是命名空间被覆盖,可以通过 Parent:: 访问。

assign

在 public 继承的情况下,子类 / 父类 对象之间可以赋值。

1
2
3
Parent parent = child;
Parent &parent = child;
Paring *parent = &child;

赋值时,child 对象会被丢弃。

非 public 继承的情况下,禁止子类赋值给父类。

virtual

父类的成员函数可以用 virtual 来修改,virtual 成员本质上是一个可变的函数指针

静态成员函数不能是 virtual,成员变量不能是 virtual

(从编译器角度,成员变量只通过 &object + offset 决定)。

当声明为子类时,并且子类的函数覆盖父类的 virtual 成员函数时,会动态绑定对应的函数指针。

此时,通过执行 指针操作 / 引用操作 完成赋值时,子类的成员函数也会被保留。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <cstdio>
struct A {
virtual void f() {
// 给定虚函数的默认
// 如果写 void f(); 则默认没有成员 f,或者是实现分离。
// 如果写 void f() = 0,则声明了一个抽象类 (abstract class)
// 抽象类不能被直接声明,只能通过子类赋值得到(强制绑定)。
puts("A");
}
void g() {
f();
}
};
struct B: A {
void f() override { // override 可以不写
puts("B");
}
};
int main() {
A a; B b;
a.f(); // A
a.g(); // A
b.f(); // B
b.g(); // B
A(b).f(); // A
A(b).g(); // A
A &c = b; // B
c.f(); // B
c.g(); // B
c.A::f(); // A
c.g(); // B
}

动态绑定的进行与函数的所有权无关。在动态绑定之后,对应函数的访问权由父类的函数决定。

虚析构

当子类被转化为父类后,子类的析构函数就消失了,这时候需要把析构函数设置成 virtual 的。

多继承

当多个父类有同一个成员时,必须通过 Parent1::member 来访问。

如果多个父类继承自同一个类,那个类会被继承多次。

虚继承

1
2
3
4
5
6
struct A {
int x;
};
struct B : public virtual A {};
struct C : public virtual A {};
struct D : public B, public C {};

此时,B, C 所继承的 A 对象是动态绑定的,且只会生成一个 A 对象。

Template

type in template

1
2
3
4
template <typename T1, typename T2 = int> // default template
T1 func(T2 a) {
;
}

template match priority:

  1. function less template and matches the type
  2. template function matches the type
  3. with inexplicit type convert
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
template<class T>
T add(const T& t1, const T& t2) {
std::cout << "T T" << std::endl;
return t1 + t2;
}
template <class T>
T add(int t1, const T& t2) {
std::cout << "int T" << std::endl;
return t1 + t2;
}
template<class T1, class T2>
T1 add(const T1& t1, const T2& t2) {
std::cout << "T1 T2" << std::endl;
return (T1)(t1 + t2);
}
double add(double t1, double t2) {
std::cout << "double double" << std::endl;
return t1 + t2;
}
int main() {
//add(1, 1); // 编译错误:可以匹配(T, T)和(int, T)
add<int,int>(1, 1); // 显式指定类型匹配T1 T2版本,输出T1 T2
add(1.0, 1); // 输出 T1 T2 add(1, 1.0); // 输出 int T
add(1.0, 1.0); // 输出 double double
add(1LL, 1LL); // 输出T T
}

Write a speicific function for a specific type:

1
2
3
4
template <class T>
void func(T x) { }
template<>
void func<int>(int t) { }

template class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <typename T = int, int size = 100>  
// default template , can be called by Type <> x;
class Type {
T x, y, arr[size];
Type():x(0),y(0) { }
void add();
friend class B<T>;
};
template <typename T> void Type<T>::add() {
return x + y;
}
template <> class Type <> {
;
}

含 template 的声明与定义分散在不同的 cpp 文件中,会导致链接期间无法找到对应函数。

正确的做法是在 .h 中声明,在 .cpp 中实现。

inheritance

1
2
3
4
5
6
7
template<class T>
class DArrayDel : public DArray<T> { };

class DoubleDArray : public DArray<double> { };

template<class T1, class T2>
class MixedDArray : public DArray<T1> { };

Design Pattern

Factory

1
2
3
4
5
6
7
8
9
10
11
12
13
class DataMakerFactory {
Factory(string a);
DataMaker* produce(...);
};
class DataMaker {
virtual int make() = 0;
};
class DataMaker_Type1: Worker{ ... };
class DataMaker_Type2: Worker{ ... };

int main() {
DataMakerFactory("target").produce() -> make();
}

Builder

1
2
3
4
5
6
7
class House {
void buildWall(Builder* ) {
}
void buildDoor(Builder* ) {
}
};

Prototype

从一个典型的父类继承过来,类似对于类的 method 的 default。

Singleton

1
2
3
4
5
6
7
8
9
10
11
12
struct Singleton {
private:
Singleton() {}
static Singleton* instance;
public:
static Singleton* getInstance() {
if (instance == NULL)
instance = new Singleton();
return instance;
}
};
Singleton* Singleton::instance = NULL;

Adapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct NewInterface {
virtual void func(Data*) = 0;
};
struct OldService {
void oldFunc(SpecialData*);
};
class Adapter : public NewInterface {
OldService* adaptee;
public:
void func(Data* data) {
SpecialData* specialData = convertToOldServiceFormat(data);
adaptee->oldFunc(specialData);
}
};

Facade

封装保留具象接口。

Bridge

例如:连接抽象层和底层。

Proxy

image-20230512083918411