C++初学-封装继承多态的实例应用

C++初学-封装继承多态的实例应用

hansyee
2024-10-11 / 0 评论 / 2 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2024年10月11日,已超过177天没有更新,若内容或图片失效,请留言反馈。

背景说明

以下程序整理于<<Essential C++>>中的面向对象编程风格的篇章,这个章节中展示的代码在某些地方其实是有问题的,例如有些常成员函数的声明其实是不能声明为常成员函数的,也可能是我理解不到位。不管怎样,本人结合作者的意图对其进行了部分修改做出一个能实际运行的程序就算成功。

类的设计

程序主要功能是计算出斐波那契数列某一位置的元素值,还包括其他一些基本功能,例如返回该数列是哪一种类型(此处只实现了斐波那契数列,实际数列有很多,可做引申)、数列的起始位置、数列的长度、可容纳元素的最大大小、打印数列所有元素。

基类

num_sequence.h

#ifndef NUM_SEQUENCE_H_INCLUDED
#define NUM_SEQUENCE_H_INCLUDED

#include <iostream>
#include <vector>
#include <typeinfo>

// 声明命名空间
namespace mycpp
{
    // C++11支持使用using为类型取别名
    // 可取代老式的typedef
    // typedef unsigned int uint_t;
    using uint_t = unsigned int;

    // 常量
    const int * const NULLPTR = NULL;

    // 基类,抽象类(含有纯虚函数)
    class num_sequence
    {
    protected:

        // 默认构造函数
        num_sequence();

        // 构造函数
        num_sequence(uint_t length, uint_t beg_pos, std::vector<uint_t> &relems);

    public:

        // 拷贝构造函数
        num_sequence(const num_sequence &rhs);

        // 赋值运算符重载函数
        // 返回引用可使用链式赋值同时避免构造临时对象,减少系统开销
        num_sequence& operator=(const num_sequence &rhs);

        // 析构函数
        // 基类的析构函数需要声明为虚函数
        // 声明虚函数的目的是告诉编译器在运行时动态的根据基类指针实际指向的对象来执行相应的析构函数
        // 未申明为虚函数的话,基类指针无论是否指向派生类,其析构函数都在编译期就被静态解析为基类的析构函数
        virtual ~num_sequence();

    public:

        // 返回数列类型名称
        // 常成员函数(执行过程中不会改变对象)
        const char* what_am_i() const;

        // 返回pos上的元素
        uint_t elem(uint_t pos);

        // 返回数列长度
        //常成员函数(执行过程中不会改变对象)
        uint_t length() const;

        // 返回数列起始位置值
        // 常成员函数(执行过程中不会改变对象)
        uint_t beg_pos() const;

        // 返回数列所支持的最大位置值
        // 静态成员函数(所有类共享一份,并不专属于哪个类的对象)
        static uint_t max_elems();

        // 将所有元素输出到os
        // 函数默认参数,默认为标准输出
        // 返回引用可使用链式输出同时避免构造临时对象,减少系统开销
        std::ostream& print(std::ostream &os = std::cout) const;

    protected:

        // 产生直到pos位置的元素
        // 纯虚函数,基类没有实现的必要,仅提供接口,依赖派生类来实现
        virtual void gen_elems(uint_t pos) = 0;

        // 检查pos位置是否为有效位置
        bool check_integrity(uint_t pos);

    protected:

        // 数列长度
        uint_t _length;

        // 数列起始位置,从1开始计数
        uint_t _beg_pos;

        // 元素最大个数
        // 静态成员、常量
        // C++11支持常量表达式
        static constexpr uint_t _max_elems = 1024;

        // 数列派生类的容器的引用
        std::vector<uint_t> &_relems;
    };

    // 内联函数(建议编译器在函数调用处直接展开,减少函数调用开销)
    inline uint_t num_sequence::length() const
    {
        return _length;
    }

    inline uint_t num_sequence::beg_pos() const
    {
        return _beg_pos;
    }

    inline uint_t num_sequence::max_elems()
    {
        return _max_elems;
    }

    inline std::ostream& operator<<(std::ostream &os, const num_sequence &ns)
    {
        return ns.print(os);
    }

}  // namespace mycpp

#endif // NUM_SEQUENCE_H_INCLUDED

num_sequence.cpp

#include "num_sequence.h"

// 使用命名空间
// using namespace mycpp;  // 全局作用域
using mycpp::num_sequence;  // 单个指定
using mycpp::uint_t;  // 单个指定


// 构造函数使用初始化列表
// 这里_relems强行绑定了一个空对象,无实际意义,仅演示默认构造函数
// 1.无参的构造函数
// 2.全部参数都有默认参数的构造函数
// 3.未实现自定义构造函数时编译器会自动生成默认构造函数
num_sequence::num_sequence() :
    _length(1),
    _beg_pos(1),
    _relems((std::vector<uint_t>&)(*NULLPTR))
{
    std::cout << "调用构造函数num_sequence::num_sequence()" << std::endl;
}


// 构造函数使用初始化列表
num_sequence::num_sequence(uint_t length, uint_t beg_pos, std::vector<uint_t> &relems) :
    _length(length),
    _beg_pos(beg_pos),
    _relems(relems)
{
    std::cout << "调用构造函数num_sequence::num_sequence(uint_t length, uint_t beg_pos, std::vector<uint_t> &relems)" << std::endl;
}


// 构造函数使用初始化列表
//num_sequence::num_sequence(const num_sequence &rhs) :
//    _length(rhs.length),
//    _beg_pos(rhs.beg_pos),
//    _relems(rhs.relems)
// C++11支持构造函数使用委托构造
num_sequence::num_sequence(const num_sequence &rhs) :
    num_sequence(rhs._length, rhs._beg_pos, rhs._relems)
{
    std::cout << "调用拷贝构造函数num_sequence::num_sequence(const num_sequence &rhs)" << std::endl;
}


num_sequence& num_sequence::operator=(const num_sequence &rhs)
{
    if (this != &rhs)  // 避免自我赋值
    {
        _length = rhs._length;
        _beg_pos = rhs._beg_pos;
        _relems = rhs._relems;
    }

    std::cout << "调用赋值运算符重载函数num_sequence::operator=(const num_sequence &rhs)" << std::endl;

    return *this;
}


num_sequence::~num_sequence()
{
    std::cout << "调用析构函数num_sequence::~num_sequence()" << std::endl;
}


const char* num_sequence::what_am_i() const
{
    // typeid运算符允许在运行时确定对象的类型,可用来确定是基类还是派生类
    // typeid运算符的返回值类型为const type_info&
    return typeid(*this).name();
}


uint_t num_sequence::elem(uint_t pos)
{
    if (!check_integrity(pos))
    {
        return 0;
    }

    return _relems[(_beg_pos - 1) + (pos - 1)];
}

std::ostream& num_sequence::print(std::ostream &os) const
{
     os << "当前的数列元素:";
    if (_relems.size())
    {

        for (uint_t i = _beg_pos - 1; i < _beg_pos + _length - 1; i++)
        {
            os << _relems[i] << ' ';
        }
    }

    os << std::endl;

    return os;
}

bool num_sequence::check_integrity(uint_t pos)
{
    if (pos <= 0 || pos > _max_elems)
    {
        std::cerr << "无效的位置:" << pos << "无法处理请求" << std::endl;

        return false;
    }

    if (pos > _relems.size())
    {
        gen_elems(pos);
    }

    return true;
}

派生类

fibonacci.h

#ifndef FIBONACCI_H_INCLUDED
#define FIBONACCI_H_INCLUDED

#include "num_sequence.h"

// 声明命名空间
namespace mycpp
{
    // 派生类
    class Fibonacci : public num_sequence
    {
    public:

        // 默认构造函数
        // 无参或所有参数都有默认参数的构造函数
        Fibonacci(uint_t length = 1, uint_t beg_pos = 1);

        // 拷贝构造函数
        Fibonacci(const Fibonacci &rhs);

        // 赋值运算符重载函数
        // 返回引用可使用链式赋值同时避免构造临时对象,减少系统开销
        Fibonacci& operator=(const Fibonacci &rhs);

        // 析构函数
        ~Fibonacci();

    protected:

        // 产生直到pos位置的元素
        // C++11支持使用override说明符指明该虚函数重写基类的虚函数
        virtual void gen_elems(uint_t pos) override;

    protected:

        // 数列容器
        // 静态成员变量(此处是声明,需要在类外定义)
        static std::vector<uint_t> _elems;
    };


    // 内联函数(建议编译器在函数调用处直接展开,减少函数调用开销)
    // 构造函数使用初始化列表
    // 此处使用基类的构造函数并不是委托构造
    inline Fibonacci::Fibonacci(uint_t length, uint_t beg_pos) :
        num_sequence(length, beg_pos, _elems)
    {
        std::cout << "调用构造函数Fibonacci::Fibonacci(uint_t length, uint_t beg_pos)" << std::endl;
    }

    inline Fibonacci::Fibonacci(const Fibonacci &rhs) :
        num_sequence(rhs)
    {
        std::cout << "调用拷贝构造函数Fibonacci::Fibonacci(const Fibonacci &rhs)" << std::endl;
    }

    inline Fibonacci& Fibonacci::operator=(const Fibonacci &rhs)
    {
        if (this != &rhs)  // 避免自我赋值
        {
            // 明确调用基类的拷贝赋值函数
            num_sequence::operator=(rhs);
        }

        std::cout << "调用赋值运算符重载函数Fibonacci& Fibonacci::operator=(const Fibonacci &rhs)" << std::endl;

        return *this;
    }

    inline Fibonacci::~Fibonacci()
    {
        std::cout << "调用析构函数Fibonacci::~Fibonacci()" << std::endl;
    }

}  // namespace mycpp

#endif // FIBONACCI_H_INCLUDED

fibonacci.cpp

#include "fibonacci.h"

// 使用命名空间
// using namespace mycpp;  // 全局作用域
using mycpp::num_sequence;  // 单个指定
using mycpp::Fibonacci;  // 单个指定
using mycpp::uint_t;  // 单个指定

std::vector<uint_t> Fibonacci::_elems;

void Fibonacci::gen_elems(uint_t pos)
{
    // 首次生成数列元素
    // 从_beg_pos开始的_length个数列元素
    if (_elems.empty())
    {
        _elems.push_back(1);
        _elems.push_back(1);

        uint_t end = _beg_pos + _length;
        uint_t i_2 = _elems[0];
        uint_t i_1 = _elems[1];

        for (uint_t i = 2 ; i < end; ++i)
        {
            uint_t elem = i_2 + i_1;
            _elems.push_back(elem);
            i_2 = i_1;
            i_1 = elem;
        }
    }

    // 继续生成不足pos位置的数列元素
    if (_elems.size() < pos + _beg_pos)
    {
        uint_t end = _elems.size();
        uint_t i_2 = _elems[end - 2];
        uint_t i_1 = _elems[end - 1];

        for (; end < pos + _beg_pos; ++end)
        {
            uint_t elem = i_2 + i_1;
            _elems.push_back(elem);
            _length++;
            i_2 = i_1;
            i_1 = elem;
        }
    }
}

测试程序

main.cpp

#include <iostream>
#include "fibonacci.h"

using namespace mycpp;

int main()
{
    std::cout << "sizeof(num_sequence)=" << sizeof(num_sequence) << std::endl;
    // sizeof(num_sequence)=24


    std::cout << "sizeof(Fibonacci)=" << sizeof(Fibonacci) << std::endl;
    // sizeof(Fibonacci)=24

    std::cout << "num_sequence::max_elems() = " << num_sequence::max_elems() << std::endl;
    // num_sequence::max_elems() = 1024

    // error 不允许使用抽象类型的对象
    // num_sequence *pbase = new num_sequence();

    // 基类的指针指向派生类,可形成动态多态
    num_sequence *pnum = new Fibonacci(10, 3);
    // 调用构造函数num_sequence::num_sequence(uint_t length, uint_t beg_pos, std::vector<uint_t> &relems)
    // 调用构造函数Fibonacci::Fibonacci(uint_t length, uint_t beg_pos)

    // RTTI(Run-Time Type Identification 运行时类型鉴定机制)
    if (typeid(*pnum) == typeid(Fibonacci))
    {
        std::cout << "pnum指向的对象是Fibonacci类型" << std::endl;
    }
    else
    {
        std::cout << "pnum指向的对象是其他类型" << std::endl;
    }
    // pnum指向的对象是Fibonacci类型
    if (dynamic_cast<Fibonacci *>(pnum))
    {
        std::cout << "pnum指向的对象是Fibonacci类型" << std::endl;
    }
    else
    {
        std::cout << "pnum指向的对象是其他类型" << std::endl;
    }
    // pnum指向的对象是Fibonacci类型

    // 强制转换,编译器无法确认是否安全,程序员需要明白这是在干什么
    if (static_cast<Fibonacci *>(pnum))
    {
        std::cout << "pnum指向的对象是Fibonacci类型" << std::endl;
    }
    else
    {
        std::cout << "pnum指向的对象是其他类型" << std::endl;
    }
    std::cout << "pnum->what_am_i() = " << pnum->what_am_i() << std::endl;
    // pnum->what_am_i() = N5mycpp9FibonacciE

    std::cout << "pnum->beg_pos() = " << pnum->beg_pos() << std::endl;
    // pnum->beg_pos() = 3

    std::cout << "pnum->length() = " << pnum->length() << std::endl;
    // pnum->length() = 10

    pnum->print();
    // 当前的数列元素:

    std::cout << "pnum->elem(10) = " << pnum->elem(10) << std::endl;
    // pnum->elem(10) = 144

    std::cout << "pnum->beg_pos() = " << pnum->beg_pos() << std::endl;
    // pnum->beg_pos() = 3

    std::cout << "pnum->length() = " << pnum->length() << std::endl;
    // pnum->length() = 10

    pnum->print();
    // 当前的数列元素:2 3 5 8 13 21 34 55 89 144

    std::cout << "pnum->elem(30) = " << pnum->elem(30) << std::endl;
    // pnum->elem(30) = 2178309

    std::cout << "pnum->beg_pos() = " << pnum->beg_pos() << std::endl;
    // pnum->beg_pos() = 3

    std::cout << "pnum->length() = " << pnum->length() << std::endl;
    // pnum->length() = 30

    pnum->print();
    // 当前的数列元素:2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309

    delete pnum;
    // 调用析构函数Fibonacci::~Fibonacci()
    // 调用析构函数num_sequence::~num_sequence()

    return 0;
}
0

评论 (0)

取消