Cpp数据结构实战开发2-基本类的构建

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Cpp数据结构实战开发2-基本类的构建相关的知识,希望对你有一定的参考价值。

  • 构建自己的类库,MxLib
    迭代开发
    单一继承树:所有类继承自Object类,规范堆对象创建时的行为
    只抛异常,不处理:使用宏抛出异常,提高可移植性
    弱耦合性:尽量不使用标准库中的类和函数,提高可移植性

顶层父类

  • 软件架构实践经验:
    尽量使用单重继承的方式进行系统设计
    尽量保持系统中只存在单一的继承树
    尽量使用组合关系代替继承关系

在MxLib中创建Objec类,所有类都继承自MxLib::Object类,统一定义动态内存申请和销毁的行为

头文件 Object.h

#ifndef OBJECT_H
#define OBJECT_H

namespace MxLib
{

class Object
{

public:
    /**
     * 以下通过重载操作符的方式统一定义动态内存申请和销毁的行为,提高代码的可移植性
    */
    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.cpp

#include "Object.h"
#include <cstdlib>  // 可以根据实际的需要更换头文件

namespace MxLib
{

void* Object::operator new(unsigned int size) throw() // 申请内存失败不会抛异常,返回 NULL
{
    return malloc(size);
}

void Object::operator delete(void* p)
{
    free(p);
}

void* Object::operator new[](unsigned int size) throw() // 申请内存失败不会抛异常,返回 NULL
{
    return malloc(size);
}

void Object::operator delete[](void* p)
{
    free(p);
}

// 父类的析构函数即使是纯虚函数也要提供实现
Object::~Object(){}

}

智能指针

  • 使用目的
    智能指针最大程度上避免堆空间内存泄露问题
  • 特点
    指针生命周期结束时主动释放堆空间
    智能指针只能用于指向堆空间中的内存
    重载指针特征符(-> 和 *)能够使用对象代替指针
    最多由一个指针标识指向一片堆空间
  • 注意
    杜绝指针指针的运算和比较
    智能指针智能用来指向堆空间的单个对象或变量

头文件 SmartPointer.h

#ifndef SMARTPOINTER_H
#define SMARTPOINTER_H

#include "Object.h"

namespace MxLib
{

template <typename T>
class SmartPointer : public Object
{
protected:
    T* pointer;

public:
    SmartPointer(T* pointer = NULL)
    {
        this->pointer = pointer;
    }

    /**
     * 在拷贝构造函数和重载赋值操作符中,使一个指针标识最多指向一片堆空间
     */
    SmartPointer(const SmartPointer<T>& obj)
    {
        this->pointer = obj.pointer;
        const_cast<SmartPointer<T>&>(obj).pointer = NULL;
    }

    SmartPointer<T>& operator = (const SmartPointer<T>& obj)
    {
        if (this != &obj)
        {
            delete this->pointer;
            this->pointer = obj.pointer;
            const_cast<SmartPointer<T>&>(obj).pointer = NULL;
        }
        return *this;
    }

    /**
     * 重载指针特征符(-> 和 *)能够使用对象代替指针
     */
    T* operator -> ()
    {
        return this->pointer;
    }

    T& operator * ()
    {
        return *(this->pointer);
    }

    bool isNull() {
        return (this->pointer == NULL);
    }

    T* get() {
        return this->pointer;
    }

    ~SmartPointer() // 只能用来指向堆空间中的单个变量或对象
    {
        delete this->pointer;
    }
};
}

#endif // SMARTPOINTER_H

异常类

使用异常机制能分离代码的正常逻辑和异常逻辑
类类型异常的匹配依旧是至上而下严格匹配,符合赋值兼容性原则
一般匹配子类异常的catch放在上部;匹配父类的放下部

头文件 Exception.h

#ifndef EXCEPTION_H
#define EXCEPTION_H

#include "Object.h"

namespace MxLib
{
// 使用宏简化代码
#define THROW_EXCEPTION(e, m) (throw e(m, __FILE__, __LINE__))

class Exception : public Object
{
protected:
    char* message;
    char* 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* getMessage() const;
    virtual const char* getLocation() const;

    // 父类的析构函数即使是纯虚函数也要提供实现
    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 NoEnoughMemoryException : public Exception
{
public:
    NoEnoughMemoryException() : Exception(0) {}
    NoEnoughMemoryException(const char* message) : Exception(message) {}
    NoEnoughMemoryException(const char* file, int line) : Exception(file, line) {}
    NoEnoughMemoryException(const char* message, const char* file, int line) : Exception(message, file, line) {}

    NoEnoughMemoryException(const NoEnoughMemoryException& e) : Exception(e) {}

    NoEnoughMemoryException& operator = (const NoEnoughMemoryException& 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 InvalidOpeartionException : public Exception
{
public:
    InvalidOpeartionException() : Exception(0) {}
    InvalidOpeartionException(const char* message) : Exception(message) {}
    InvalidOpeartionException(const char* file, int line) : Exception(file, line) {}
    InvalidOpeartionException(const char* message, const char* file, int line) : Exception(message, file, line) {}

    InvalidOpeartionException(const InvalidOpeartionException& e) : Exception(e) {}

    InvalidOpeartionException& operator = (const InvalidOpeartionException& e)
    {
        Exception::operator = (e);
        return *this;
    }
};

}

#endif // EXCEPTION_H

源文件 Exception.cpp

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

using namespace std;

namespace MxLib
{

void Exception::init(const char* message, const char* file, int line)
{
    //无法确定 message 字符串的所处的内存空间,需要在堆空间中拷贝 message 字符串以保证
    this->message = strdup(message);

    if(NULL != file)
    {
        char sl[16] = {0};

        // 把行号转为字符存放在sl数组里
        itoa(line, sl, 10);

        this->location = static_cast<char*>(malloc(strlen(file) + strlen(sl) + 2));

        // 动态内存申请失败不采取行动
        if (NULL != this->location)
        {
            this->message =strcpy(this->message, file);
            this->message=strcat(this->message, ":");
            this->message=strcat(this->message, sl);
        }
    }
    else
    {
        this->location = NULL;
    }
}

Exception::Exception(const char* message)
{
    init(message, NULL, 0);
}

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

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

/**
 * 拷贝构造函数和赋值重载符使用深拷贝
 */
Exception::Exception(const Exception& e)
{
    this->message = strdup(e.message);
    this->location = strdup(e.location);
}

Exception& Exception::operator= (const Exception& e)
{
    if (this != &e)
    {
        free(this->message);
        free(this->location);

        this->message = strdup(e.message);
        this->location = strdup(e.location);
    }
    return *this;
}

const char* Exception::getMessage() const
{
    return this->message;
}

const char* Exception::getLocation() const
{
    return this->location;
}

Exception::~Exception()
{
    free(this->message);
    free(this->location);
}
}

以上是关于Cpp数据结构实战开发2-基本类的构建的主要内容,如果未能解决你的问题,请参考以下文章

[Java 并发编程实战] 设计线程安全的类的三个方式(含代码)

c_cpp Atlas300代码片段

c_cpp 加载源图像固定用法(代码片段,不全)

Express实战 - 应用案例- realworld-API - 路由设计 - mongoose - 数据验证 - 密码加密 - 登录接口 - 身份认证 - token - 增删改查API(代码片段

Python工业项目实战01:项目介绍及环境构建

c_cpp ROOT TH1类的简化基本功能