第13课 类族结构的进化

1. 遵循经典设计准则

(1)DTLib中所有类位于单一的继承树

(2)改进的关键点

  ①Exception类继承自Object类:堆空间中创建异常对象失败时,返回NULL指针。

  ②新增InvalidOperationException类:成员函数调用时,如果状态不正确则抛出异常。

  ③SmartPointer类继承自Object类:堆空间中创建智能指针对象失败时,返回NULL指针。

2. DTLib的开发方式和注意事项

(1)迭代开发:每次完成一个小的目标,持续开发,最终打造可复用类库

(2)单一继承树:所有类都继承自Object,规范堆对象创建时的行为

(3)只抛异常,不处理异常:使用THROW_EXCEPTION抛出异常,提高可移植性。这意味着DTLib库中无需使用try-catch语句,这样就可以比较方便地将代码移植到老的编译器,所要做的工作只需将THROW_EXCEPTION宏后半部分注释掉,即#define THROW_EXCEPTION(e, m) //(throw e(m, __FILE__, __LINE__)))。同时由于DTLib库只抛异常,而不处理异常,这就把异常相关的处理从DTLib中剔除,让使用库的人自己去处理异常

(4)弱耦合性:尽量不使用标准库中的类和函数,提高可移植性。

【编程实验】类族结构的进化

//Object.h

#ifndef _OBJECT_H_
#define _OBJECT_H_

namespace DTLib
{

class Object
{
public:
    //以下四个重载函数用于统一不同编译器new失败时的结果不同的问题。
    //throw()表示不抛出异常,即如果申请内请失败时,统一返回NULL而不抛异常
    void* operator new(unsigned int size) throw();
    void operator delete(void* p);
    void* operator new[](unsigned int size) throw();
    void operator delete[](void* p);

    virtual ~Object() = 0;
};

}

#endif // _OBJECT_H_

//Object.cpp

#include "Object.h"
#include <cstdlib>
#include <iostream>

using namespace std;

namespace DTLib {

void * Object::operator new(unsigned int size)  throw()
{
    cout <<"Object::operator new: " << size << endl;
    return malloc(size);
}

void Object::operator delete(void *p)
{
    cout <<"Object::operator delete: " << p << endl;
    free(p);
}

void *Object::operator new[](unsigned int size) throw()
{
    //当用new Test[5]时,只须传入数组元素的个数,编译器会向operator new[](...)函数的参数
    //传入5*sizeof(Test) + sizeof(unsigned int),其中的sizeof(unsigned int)为额外
    //空间,用于保存元素的个数。
    cout <<"Object::operator new[]: " << size << endl;
    return malloc(size);
}

void Object::operator delete[](void *p)
{
    cout <<"Object::operator delete[]: " << p << endl;
    free(p);
}

Object::~Object()
{

}

}

//SmartPointer.h

#ifndef _SMARTPOINTER_H_
#define _SMARTPOINTER_H_

#include "Object.h"

namespace DTLib {

//智能指针
template<typename T>
class SmartPointer : public Object
{
    T* m_pointer;
public:
    //构造函数
    SmartPointer(T* p = NULL)
    {
        m_pointer = p;
    }

    //拷贝构造函数
    SmartPointer(const SmartPointer<T>& obj)
    {
        m_pointer = obj.m_pointer;

        //所有权转移,使得同一时刻只能由一个指针指向堆空间
        const_cast<SmartPointer<T>&>(obj).m_pointer = NULL;
    }

    //重载赋值操作符
    SmartPointer<T>& operator=(const SmartPointer<T>& obj)
    {
        if(this != &obj){
            delete m_pointer;
            m_pointer = obj.m_pointer;

            //所有权转移
            const_cast<SmartPointer<T>&>(obj).m_pointer = NULL;
        }
        return *this;
    }

    //重载->操作符
    T* operator->()
    {
        return m_pointer;
    }

    //重载*操作符
    T& operator*()
    {
        return *m_pointer;
    }

    bool isNull()
    {
        return (m_pointer == NULL);
    }

    T* get()
    {
        return m_pointer;
    }

    ~SmartPointer()
    {
        delete m_pointer;
    }
};

}

#endif // _SMARTPOINTER_H_

//Exception.h

#ifndef _EXCEPTION_H_
#define _EXCEPTION_H_
#include "Object.h"

namespace DTLib
{

#define THROW_EXCEPTION(e, m) (throw e(m, __FILE__, __LINE__))

class Exception : public Object
{
protected:
    char* m_message;
    char* m_location;

    void init(const char* message, const char* file, int line);

public:
    //构造函数
    Exception(const char* message);
    Exception(const char* file, int line);
    Exception(const char *message, const char *file, int line);
    //拷贝构造函数
    Exception(const Exception& e);
    //重载赋值操作符
    Exception& operator=(const Exception& e);

    virtual const char* message() const;
    virtual const char* location() const;

    //注意:
    //(1)析构函数是较为特殊的函数,一旦定义了析构函数,不管这个函数是不是纯虚函数,就
    //必须提供实现。因为,对象在销毁时,最后都会调用父类的析构函数。如果父类不提供实现,
    //当对象销毁过程中调用到父类析构函数时,就找不到析构函数,也就不知该如何析构下去。
    //因此,尽管这里将析构函数声明为纯虚函数,但Exception类仍提供析构函数的实现。以便
    //最后正确释放掉m_message和m_location所指的堆空间.
    //(2)此外,声明为纯虚函数,可以让该类只能作为接口使用,而且也强迫子类必须
    //提供析构函数的实现。
    virtual ~Exception() = 0; //纯虚函数
};

//计算异常类
class ArithmeticException: public Exception
{
public:
    ArithmeticException():Exception(0){}
    ArithmeticException(const char* message):Exception(message){}
    ArithmeticException(const char*file, int line):Exception(file, line){}
    ArithmeticException(const char *message, const char* file, int line):Exception(message, file, line){}

    ArithmeticException(const ArithmeticException& e): Exception(e){}
    ArithmeticException& operator=(const ArithmeticException& e)
    {
        Exception::operator =(e);

        return *this;
    }
};

//空指针异常类
class NullPointerException: public Exception
{
public:
    NullPointerException():Exception(0){}
    NullPointerException(const char* message):Exception(message){}
    NullPointerException(const char*file, int line):Exception(file, line){}
    NullPointerException(const char *message, const char* file, int line):Exception(message, file, line){}

    NullPointerException(const NullPointerException& e): Exception(e){}
    NullPointerException& operator=(const NullPointerException& e)
    {
        Exception::operator =(e);

        return *this;
    }
};

//越界异常类
class IndexOutOfBoundsException: public Exception
{
public:
    IndexOutOfBoundsException():Exception(0){}
    IndexOutOfBoundsException(const char* message):Exception(message){}
    IndexOutOfBoundsException(const char*file, int line):Exception(file, line){}
    IndexOutOfBoundsException(const char *message, const char* file, int line):Exception(message, file, line){}

    IndexOutOfBoundsException(const IndexOutOfBoundsException& e): Exception(e){}
    IndexOutOfBoundsException& operator=(const IndexOutOfBoundsException& e)
    {
        Exception::operator =(e);

        return *this;
    }
};

//内存不足异常类
class NotEnoughMemoryException: public Exception
{
public:
    NotEnoughMemoryException():Exception(0){}
    NotEnoughMemoryException(const char* message):Exception(message){}
    NotEnoughMemoryException(const char*file, int line):Exception(file, line){}
    NotEnoughMemoryException(const char *message, const char* file, int line):Exception(message, file, line){}

    NotEnoughMemoryException(const NotEnoughMemoryException& e): Exception(e){}
    NotEnoughMemoryException& operator=(const NotEnoughMemoryException& e)
    {
        Exception::operator =(e);

        return *this;
    }
};

//参数错误异常类
class InvalidParameterException: public Exception
{
public:
    InvalidParameterException():Exception(0){}
    InvalidParameterException(const char* message):Exception(message){}
    InvalidParameterException(const char*file, int line):Exception(file, line){}
    InvalidParameterException(const char *message, const char* file, int line):Exception(message, file, line){}

    InvalidParameterException(const InvalidParameterException& e): Exception(e){}
    InvalidParameterException& operator=(const InvalidParameterException& e)
    {
        Exception::operator =(e);

        return *this;
    }
};

//无效操作异常类(成员函数调用时,如果状态不正确则抛出异常)
class InvalidOperationException: public Exception
{
public:
    InvalidOperationException():Exception(0){}
    InvalidOperationException(const char* message):Exception(message){}
    InvalidOperationException(const char*file, int line):Exception(file, line){}
    InvalidOperationException(const char *message, const char* file, int line):Exception(message, file, line){}

    InvalidOperationException(const InvalidOperationException& e): Exception(e){}
    InvalidOperationException& operator=(const InvalidOperationException& e)
    {
        Exception::operator =(e);

        return *this;
    }
};

}

#endif // _EXCEPTION_H_

//Exception.cpp

#include "Exception.h"
#include <cstring>
#include <cstdlib>

using namespace std;

namespace DTLib
{

void Exception::init(const char *message, const char *file, int line)
{
    m_message = strdup(message); //复制message的内容

    m_location = NULL;

    if(file != NULL){
        char sl[16]={0};
        itoa(line, sl, 10);//将整数line转为字符串,其中的10表示转换为十进制格式

        //m_location的格式为:file:line\0;
        m_location = static_cast<char*>(malloc(strlen(file) + strlen(sl) + 2));

        //注意:申请内存失败时无须再抛NotEnoughMemoryException异常,从宏观上看,父类
        //是无法抛出子类型的异常的。从逻辑上看也不能抛出这个异常,因为当父类构造时出现异常时,
        //如果去抛出子类异常,则必然需要构造子类,但这又得先调用父类构造函数(会再一次产
        //生异常,从而造成Exception构造函数的递归调用,从而造成死循环!)
        if(m_location != NULL){   //内存申请成功
            m_location = strcpy(m_location, file);
            m_location = strcat(m_location, ":");
            m_location = strcat(m_location, sl);
        }
    }
}

//构造函数
Exception::Exception(const char* message)
{
    init(message, NULL, 0);
}

Exception::Exception(const char* file, int line)
{
    init(NULL, file, line);
}

Exception::Exception(const char *message, const char *file, int line)
{
    init(message, file, line);
}

//拷贝构造函数
Exception::Exception(const Exception& e)
{
    //深拷贝
    m_message = strdup(e.m_message);
    m_location = strdup(e.m_location);
}

//重载赋值操作符
Exception& Exception::operator=(const Exception& e)
{
    if(this != &e){ //防止自赋值
        free(m_message);
        free(m_location);

        //深拷贝
        m_message = strdup(e.m_message);
        m_location = strdup(e.m_location);
    }

    return *this;
}

const char* Exception::message() const
{
    return m_message;
}

const char* Exception::location() const
{
    return m_location;
}

Exception::~Exception()
{
    free(m_message);
    free(m_location);
}

}

//main.cpp

#include <iostream>
#include "SmartPointer.h"
#include "Exception.h"

using namespace std;
using namespace DTLib;

int main()
{
    SmartPointer<int>* sp = new SmartPointer<int>();

    delete sp;

    InvalidOperationException* e = new InvalidOperationException();

    delete e;

    return 0;
}
/*输出结果
Object::operator new: 8
Object::operator delete: 0x7a12a8
Object::operator new: 12
Object::operator delete: 0x7a12a8
*/

3. 第一阶段学习总结

(1)数据结构与算法之间的关系

(2)算法效率的度量方法

(3)DTLib的基础设施构建:顶层父类、智能指针、异常类…

时间: 2024-10-18 16:26:52

第13课 类族结构的进化的相关文章

C#基础 类、结构、抽象类、接口的相关概念

先来简单说一下他们各自的含义是什么: 类(class):类类型是由字段数据(成员变量)和操作字段数据的成员(属性.方法.构造函数.事件等)所构成的自定义类型.其中字段数据表示类实例(对象)的 状态.2.结构(struct):C#中结构类型和类类型在语法上非常相似,他们都是一种数据结构,都可以 包括数据成员和方法成员. 3.抽象类:①:抽象类是特殊的类,只是不能被实例化:②:除此以外,具有类的其他特性:③:重要的是抽象类可以包括抽象方法,这是普通类所不能的.抽象方法只能声明于抽象类中,且不包含任何

Swift面向对象基础(上)——Swift中的类和结构体(上)

学习来自<极客学院> 1 import Foundation 2 3 //1.定义类和结构体 4 /* 5 [修饰符]calss 类名{ 6 零到多个构造器 7 零到多个属性 8 零到多个方法 9 零到多个下标 10 } 11 修饰符可以是:private public internal final 12 */ 13 14 /*定义结构体 15 [修饰符]struct 结构体名{ 16 17 } 18 修饰符可以是:private public internal 19 */ 20 21 /*

Hadoop源码分析之NameNode的目录构成与类继承结构

与DataNode节点类似,NameNode节点也有自己的目录和类继承结构,但是NameNode节点的目录结构比DataNode节点就简单多了,下面就来简单分析NameNode节点的目录构成与类继承结构. NameNode的目录构成 在NameNode节点运行时,维护着整个HDFS中的文件及目录信息,如果NameNode出现故障,如掉电或进程崩溃,那么内存中的信息将全部丢失,因此必须将内存中的信息实时的保存在磁盘中,如果NameNode节点崩溃,那么在下次启动NameNode节点时可以重新从磁盘

3/类与结构区别

  C#结构和类的六点区别   引言··· 1 区别一:存储类型··· 3 堆和栈:··· 3 结构和类的存储类型:··· 3 区别二:继承性··· 4 区别三:初始化··· 5 区别四:构造函数··· 5 区别五:析构函数··· 7 区别六:关键字··· 7 类和结构的使用选择:··· 7 参考:··· 8 1.一览表:··· 8 2.结构和类的区别:··· 8 3.结构和类的异同:··· 8 4.源代码:··· 8 Struct,cs· 8 Class.cs· 10 Program.cs·

C++ 类族的设计

- 类族的设计]    按以下的提示,由基类的设计和测试开始,逐渐地完成各个类的设计,求出圆格柱体的表面积.体积并输出并且完成要求的计算任务:    (1)先建立一个Point(点)类,包含数据成员x,y(坐标点),实现需要的成员函数,并设计main函数完成测试:    (2)以Point为基类,派生出一个Circle(圆)类,增加数据成员r(半径),以及求面积的成员函数area,实现其他需要的成员函数,设计main函数完成测试:    (3)再以Circle类为直接基类,派生出一个Cylind

2017-9-23C#笔记(类的索引,事件,运算符,this访问器,派生,分部类,抽象类,封闭类,静态类,类与结构的不同)

1.类的索引 索引是一组get和set锋访问器,支持按照饮用数组元素的方法来引用对象.索引通常便是多个数据成员,并且它总是以雷类的事例成员的方式存在.声明索引的方法: 返回类型     this  [参数列表] { Get {    } set {        } } 例如:using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplicat

[转]站在OC的基础上快速理解Swift的类与结构体

(阅读此文章前,您已经有一定的Object-C语法基础了!) 2014年,Apple推出了Swift,最近开始应用到实际的项目中. 首先我发现在编写Swift代码的时候,经常会遇到Xcode不能提示,卡顿,直接闪退等问题,尤其是在Swift和OC混编时.(不知道其他开发者是否也有这样的经历,但是我相信这样的问题,很快会得到解决) 然后感觉Swift并不像网上很多朋友说的那样简单.有很多细节问题是值得注意的,甚至有很多思路是颠覆了传统的开发语言的!又有很多特性是结合了其他编程语言的优点! Swif

在OC的基础上快速理解Swift的类与结构体

Swift中,类和结构体都是对数据和方法进行封装的常用做法!首先我们来看看他们的共同之处: 都可以有属性和方法: 都有构造器: 都支持附属脚本: 都支持扩展: 都支持协议. 然后我们来看看他们的不同之处: 类有继承: 结构体有一个自动生成的逐一初始化构造器: 在做赋值操作时,结构体总是被拷贝(Array有特殊处理): 结构体可以声明静态的属性和方法: 从设计模式的角度来分析,类的设计更侧重于对功能的封装,而结构体的设计更侧重于对数据的封装.(汽车与书架来区分类与结构体) 一.构造过程 1. 默认

安卓学习第13课——BaseAdapter

BaseAdapter创建这么一个对象,需要些四个方法. int getCount(); Object getItem(int position); long getItemId(int position);View getView(int position, View convertView, ViewGroup parent);(1)列表中的项数(2)返回值的列表内容(3)获得postion处的列表项的ID(4)该列表项里的组件 package com.example.baseadapter