[OOP]重载类型转换操作符

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

class Fraction
{
public:
    Fraction(): num(1), den(1)
    {
    }
    Fraction(int n, int d) : num(n), den(d)
    {
    }

    // 类型转换操作符
    operator double()
    {
        return (double)num/den;
    }

public:
    int num;
    int den;
};


int main()
{
    Fraction fa(4,5);
    double value = (double) fa;
    printf("%f \n", value);

    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

class Text
{
public:
    Text(const char* str)
    {
        // 申请一块内存, 保存此字符串
        m_size = strlen(str) + 1;
        m_buf = new char[m_size];
        strcpy(m_buf, str);		
    }
    ~Text()
    {
        // 释放此字符串
        delete [] m_buf;
    }

    // 类型转换操作符
    operator const char* ()
    {
        return m_buf;
    }

private:
    int m_size;
    char* m_buf;
};

int main()
{
    Text t1("hello world");
    
    const char* txt = (const char*) t1;
    printf("text: %s \n", txt);
    return 0;
}

 

[OOP]重载关系操作符

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

class Fraction
{
public:
    Fraction(): num(1), den(1)
    {
    }
    Fraction(int n, int d) : num(n), den(d)
    {
    }

    bool operator == (const Fraction& other)
    {
        if(num * other.den  == den * other.num)
        {
            return true;
        }
        return false;
    }

public:
    int num;
    int den;
};


int main()
{
    Fraction fa(2, 3);
    Fraction fb(4, 6); // 4/6 2/3
    if(fa == fb)
    {
        printf("equal\n");
    }
    else
    {
        printf("not equal\n");
    }

    return 0;
}

 

[OOP]重载元素操作符

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

class Text
{
public:
    Text(const char* str)
    {
        // 申请一块内存, 保存此字符串
        m_size = strlen(str) + 1;
        m_buf = new char[m_size];
        strcpy(m_buf, str);		
    }
    ~Text()
    {
        // 释放此字符串
        delete [] m_buf;
    }

    char& operator[] (int index)
    {
        return m_buf[index];
    }

private:
    int m_size;
    char* m_buf;
};

int main()
{
    Text t1("helloworld");
    char ch = t1[0];
    t1[0] = 'H';
    return 0;
}

 

[OOP]重载算术操作符

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

class Fraction
{
public:
    Fraction(): num(1), den(1)
    {
    }
    Fraction(int n, int d) : num(n), den(d)
    {
    }

    // 重载加号操作符
    Fraction operator + (const Fraction& other)
    {
        Fraction result;
        result.den = den * other.den; // 分母相乘
        result.num = num*other.den + den * other.num; // 分子交叉相乘
        return result;
    }

public:
    int num;
    int den;
};

// 重载全局操作符
Fraction operator + (const Fraction& a, const Fraction& b)
{
    Fraction result;
    result.den = a.den * b.den; // 分母相乘
    result.num = a.num * b.den + a.den * b.num; // 分子交叉相乘
    return result;
}

int main()
{
    Fraction fa(2, 3);
    Fraction fb(3, 5);
    Fraction fc = fa + fb;

    return 0;
}

 

[OOP]友元函数

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

class Object
{
    friend void Print(Object* p);

public:
    Object(int v) : value(v)
    {
    }
private:
    int value;
};

void Print(Object* p)
{
    printf("value: %d \n", p->value);
}


int main()
{
    Object obj(10);
    Print(&obj);
    return 0;
}

 

[OOP]深度拷贝

//当以下情况发生时,需要添加拷贝构造函数。示例:

class Text
{
public:
    Text(const char* str)
    {
        // 申请一块内存, 保存此字符串
        m_size = strlen(str) + 1;
        m_buf = new char[m_size];
        strcpy(m_buf, str);		
    }
    ~Text()
    {
        // 释放此字符串
        delete [] m_buf;
    }
private:
    int m_size;
    char* m_buf;
};


int main()
{
      // 定义第一个对象
    Text t1("helloworld");

      // 第二个对象以t1为蓝本进行拷贝
    Text t2(t1);

    return 0;
}

出什么会出错?

// 对象创建
对象t1.m_buf,指向一块内存
对象t2拷贝了t1, t2.m_buf指向了同一块内存

// 对象析构
对象t1析构, delete [] m_buf;
对象t2析构,delete [] m_buf;出错,此块内存已经被delete

错误的根本原因:应该拷贝其数据,而不是拷贝其指针。

 

(1) 正规解决方法
添加拷贝构造函数,拷贝其具体的数据

Text(const Text& other)
{
m_size = other.m_size;
m_buf = new char[m_size];
strcpy(m_buf, other.m_buf);
}
此种情况称为“深度拷贝”

(2) 省事的办法
禁止用户进行拷贝构造,将拷贝构造函数设定为private。

private:
Text(const Text& other)
{
}

[OOP]拷贝构造函数

拷贝构造函数是一种特殊的构造函数。copy constructor
它是构造函数,所以函数名是类名、没有返回值
它是特殊的构造函数:参数形式是固定的

class Object
{
public:
Object( const Object& other );
};

拷贝构造函数的含义: 以一个对象为蓝本,来构造另一个对象。

Object b;
Object a(b); // 或写成 Object a = b;

称作:以b为蓝本,创建一个新的对象a。

(a是b的一个拷贝/备份,两者内容完全相同)

 

拷贝构造函数从来不显式调用,而是由编译器隐式地调用。
在以下三种情况:
(1)定义对象
Object a;
Object b(a); // 或写成 Object b = a;

(2)动态创建对象
Object a;
Object* p = new Object(a);

(3)函数的传值调用
void Test(Object obj);

构造:
Object a;
Object b = a; // 或写作 Object b(a);
// 此为“构造”,在创建对象的时候给初值,拷贝构造函数被调用

赋值:
Object a(1,2);
Object b;
b = a; // 此为“赋值”,不会调用拷贝构造函数

在拷贝构造函数,可以访问参数对象的任意成员

因为它们是同类,所以访问不受限制。

Object(const Object& other)
{
this->a = other.a;
this->b = other.b;
}

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


class Object
{
public:
    Object(int a, int b)
    {
        this->a = a;
        this->b = b;
    }
    Object(const Object& other)
    {
        printf("in copy constructor...\n");
        this->a = other.a;
        this->b = other.b;
    }
private:
    int a;
    int b;
};

void Test(Object obj) // Object obj(x)
{

}

int main()
{
    Object x(1,2);

    Test(x);

// 	Object y(x); // copy
// 	Object* p = new Object(x);
// 
// 	delete p;
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

class Base
{
public:
    int dddd;
};
class Object : public Base
{
public:
    Object(int a, int b)
    {
        this->a = a;
        this->b = b;
    }
    Object(const Object& other):Base(other)
    {
        this->a = other.a;
        this->b = other.b;
    }
private:
    int a;
    int b;
};

int main()
{
    Object objx(1,2);
    objx.dddd = 123;

    Object objy(objx);

    return 0;
}

 

[OOP]虚拟继承

子类可以重写从父类继承而来的函数 (overwriting)

class Parent
{
public:
    void Test();
};
class Child : public Parent
{
public:
    void Test();
};


Child ch;
ch.Test(); // 调用的是子类的Test()函数

如果重写的时候,还是要嵌入调用一下父类的函数,怎么办?

void Child::Test()
{
Parent::Test(); // 显式地调用父类的函数
}

可以将父类指针指向一个子类的对象,这是完全允许的。

例如,
// 左侧为Tree*,右侧为AppleTree*
Tree* p = new AppleTree();

从普通的逻辑来讲,苹果树是一种树,因而可以把AppleTree*视为一种Tree*

从语法本质上讲,子类对象的前半部分就是父类,因而可以将子类对象的指针直接转化为父类。

有父类和子类:
class Parent
{
public:
    int a;
};

class Child : public Parent
{
public:
    int b;
};

int main()
{
    Child ch;
    ch.a = 0x11111111;
    ch.b = 0x22222222;

    Parent* p = &ch; // p指向的对象是Child*
    printf("Parent::a = %d \n", p->a);

    return 0;
}
所以,从直观感觉到内在机理都允许这么做

问题:考虑以下情况,
Parent* p = new Child();
p->Test();

那么,此时调用的Test()是父类的、还是子类的?
指针p的类型是Parent*
指针p指向的对象是Child*

调用者的初衷:因为p指向的是对象是子类对象,所以应该调用子类的Test()。

 

当一个成员函数需要子类重写,那么在父类应该将其声明为virtual。
(有时将声明为virtual的函数为“虚函数”)

例如
class Parent
{
public:
virtual void Test();
};

virtual本身表明该函数即将被子类重写。

 

加virtual关键字是必要的。

考虑以下情况,
Parent* obj = new Child(); // 语法允许,合乎情理
obj->Test();

此时,如果Test()在父类中被声明为virtual,是调用的是子类的Test()。

这解释了virtual的作用:根据对象的实际类型,调用相应类型的函数。

 

注意:
(1)只需要在父类中将函数声明为virtual,子类自动地就是virtual了。

(2)即将被重写的函数添加virtual,是一条应该遵守的编码习惯。

 

当一个类被继承时,应该将父类的析构函数声明为virtual,
否则会有潜在的问题。

class Parent
{
virtual ~Parent(){} // 声明为virtual
};

考虑以下场景:

Parent* p = new Child();
delete p; // 此时,调用的是谁的析构函数?

如果析构函数没有标识为virtual,则有潜在的隐患,并有可能直接导致程序崩溃。(资源没有被释放,并引申一系列问题)

[OOP]类的继承

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

class Tutorial
{
public:
    char name[32];
    char author[16];
public:
    void ShowInfo()
    {
        printf("Tutorial: %s, %s \n", name, author);
    }
protected:
    int abc;
};

class VideoTutorial : public Tutorial
{
public:
    void Play()
    {		
        printf("Playing...abc=%d\n", abc);
    }

public:
    char url[128]; // 在线观看的URL地址 
    int visits; // 播放量
};


int main()
{
    VideoTutorial cpp_guide;
    strcpy(cpp_guide.name, "C/C++学习指南");
    strcpy(cpp_guide.author, "邵发");
    cpp_guide.ShowInfo();

    strcpy(cpp_guide.url, "http://111111");
    cpp_guide.visits = 10000000;

//	cpp_guide.abc = 123;
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

class Parent
{
public:
    Parent()
    {
        ddd = 0xAAAAAAAAA;
    }
    int a;
private:
    int ddd;
};

class Child : public Parent
{
public:
    int b;
};

int main()
{
    Child c;
    c.a = 0x11111111;
    c.b = 0x22222222;

//	cpp_guide.abc = 123;
    return 0;
}

 

[OOP]析构函数

DataStore.h

struct Student
{
    int id;
    char name[16];
    Student* next;
};

class DataStore
{
public:
    DataStore();
    ~DataStore();

public:
    void Add(const Student* data);
    Student* Find(int id);
    void Print();

private:
    Student m_head;
};

DataStore.cpp

#include <stdio.h>
#include <stdlib.h>
#include "DataStore.h"

DataStore::DataStore()
{
    m_head.next = NULL;
}

DataStore::~DataStore()
{
    Student* p = m_head.next;
    while(p)
    {
        Student* next = p->next;
        free(p);
        p = next;
    }
}


void DataStore::Add(const Student* data)
{
    // 创建对象、复制数据
    Student* copy = (Student*)malloc(sizeof(Student));
    *copy = *data;

    // 插入一个对象到链表中
    Student* cur = m_head.next; // 当前节点current
    Student* pre = &m_head;  // 上一个节点previous
    while(cur)
    {		
        if(copy->id < cur->id) // 找到这个位置
            break;

        pre = cur;
        cur = cur->next;  // 找到最后一个对象
    }

    // 插入到pre节点的后面
    copy->next = pre->next;
    pre->next = copy;
}

Student* DataStore::Find(int id)
{
    Student* p = m_head.next; 
    while(p)
    {
        if(p->id == id)
            return p;

        p = p->next; // 下一个对象
    }
    return NULL;
}

void DataStore::Print()
{
    Student* p = this->m_head.next; 
    while(p)
    {
        printf("ID: %d, name: %s\n", p->id, p->name);
        p = p->next; // 下一个对象
    }
}