Essential C++学习笔记及源代码(第6章 template编程风格)

第6章介绍了C++语言中使用template进行模板元编程的方式

导言:Bjarne Stroustrup(C++创造者)拟好C++语言中关于template的原始设计后,将template称为被参数化的类型(parameterized type):称其参数化是因为,类型相关信息可自template定义中剥离,称其类型则是因为,每一个类模板或者函数模板基本上都随着它所作用或它所内含的类型而有性质上的变化(因此这些类模板或函数模板本身就像是某种类型)。template所接受的类型是由用户在使用中所指定的。

其后不久,Stroustrup将名称更改为比较通俗顺口的template(模板)。Template定义扮演的是“处方”角色,能根据用户指定的特定值或特定类型,自动产生一个函数或类。

本章作者使用了二叉查找树来介绍template的设计,使得元素类型在用户使用时指定,从而得到的是一个与类型无关的二叉查找树,而后又用了第5章的一个数列类来进行template的改写,说明了2种设计能够产生同样的结果。

附上笔者所写的课后练习答案。
//ch6_main.cpp

#include <iostream>
#include <string>
#include <vector>
#include <utility>
#include <typeinfo>
using namespace std;

//len是数列长度, beg_pos是数列起始位置, 把2个属性作为模板参数后可以动态指定;
template <int len, int beg_pos>
class num_sequence;

template <int len, int beg_pos>
ostream &operator<<(ostream &os, const num_sequence<len, beg_pos> &ns);

template <int len, int beg_pos>
class num_sequence
{
public:
    virtual ~num_sequence(){};
    int elem(int pos) const { return !check_integrity(pos, _pelems->size()) ? 0 : (*_pelems)[pos - 1]; }
    const char *what_am_i() const { return typeid(*this).name(); }
    static int max_elems() { return _max_elems; }
    ostream &print(ostream &os = cout) const;

protected:
    virtual void gen_elems(int pos) const = 0;
    bool check_integrity(int pos, int size) const;
    num_sequence(vector<int> *pe) : _pelems(pe) {}
    enum
    {
        _max_elems = 1024
    };
    vector<int> *_pelems;
};

template <int len, int beg_pos>
ostream &operator<<(ostream &os, const num_sequence<len, beg_pos> &ns)
{
    return ns.print(os);
}

template <int length, int beg_pos>
bool num_sequence<length, beg_pos>::check_integrity(int pos, int size) const
{
    if (pos <= 0 || pos > max_elems())
    {
        cerr << "!! invalid position: " << pos;
        cerr << " Cannot honor request\n";
        return false;
    }
    if (pos > size)
    {
        gen_elems(pos);
    }
    return true;
}

template <int length, int beg_pos>
ostream &num_sequence<length, beg_pos>::print(ostream &os) const
{
    int elem_pos = beg_pos - 1;
    int end_pos = elem_pos + length;

    if (!check_integrity(end_pos, _pelems->size()))
    {
        return os;
    }
    os << "( " << beg_pos << " , ";
    os << length << " ) ";
    while (elem_pos < end_pos)
    {
        os << (*_pelems)[elem_pos++] << ' ';
    }
    return os;
}

template <int length, int beg_pos = 1>
class Fibonacci : public num_sequence<length, beg_pos>
{
public:
    Fibonacci() : num_sequence<length, beg_pos>(&_elems) {}

protected:
    virtual void gen_elems(int pos) const;
    static vector<int> _elems;
};

template <int length, int beg_pos>
vector<int> Fibonacci<length, beg_pos>::_elems;

template <int length, int beg_pos>
void Fibonacci<length, beg_pos>::gen_elems(int pos) const
{
    if (pos <= 0 || pos > num_sequence<length, beg_pos>::max_elems())
    {
        return;
    }
    if (_elems.empty())
    {
        _elems.push_back(1);
        _elems.push_back(1);
    }
    if (_elems.size() < pos)
    {
        int ix = _elems.size();
        int n_2 = _elems[ix - 2];
        int n_1 = _elems[ix - 1];
        for (int elem; ix < pos; ++ix)
        {
            elem = n_2 + n_1;
            _elems.push_back(elem);
            n_2 = n_1;
            n_1 = elem;
        }
    }
}

int main()
{
    /*
        fib1: ( 1 , 8 ) 1 1 2 3 5 8 13 21
        fib2: ( 8 , 8 ) 21 34 55 89 144 233 377 610
        fib3: ( 8 , 12 ) 21 34 55 89 144 233 377 610 987 1597 2584 4181
    */
    Fibonacci<8> fib1;
    Fibonacci<8, 8> fib2;
    Fibonacci<12, 8> fib3;

    cout << "fib1: " << fib1 << endl;
    cout << "fib2: " << fib2 << endl;
    cout << "fib3: " << fib3 << endl;

    return 0;
}

//------------------------------------------------------------

//binary_tree.cpp

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

template <typename elemType>
class BinaryTree;

template <typename valType>
class BTnode;

template <typename valType>
class BTnode
{
public:
    BTnode(const valType &val);                             //值构造函数;
    const valType &value() const { return _val; }           //返回树中元素的引用;
    int occurs() const { return _cnt; }                     //返回树中元素的出现次数;
    void remove_value(const valType &, BTnode *&);          //删除元素;
    void insert_value(const valType &);                     //添加元素;
    bool find_value(const valType &) const;                 //查找元素;
    void preorder(BTnode *, ostream &) const;               //先序遍历;
    void inorder(BTnode *, ostream &) const;                //中序遍历;
    void postorder(BTnode *, ostream &) const;              //后序遍历;
    static void lchild_leaf(BTnode *leaf, BTnode *subtree); //找到最左子树;

private:
    friend class BinaryTree<valType>; //友元类;
    int _cnt;                         //元素出现次数;
    valType _val;                     //元素值;
    BTnode *_lchild;                  //左子树指针;
    BTnode *_rchild;                  //右子树指针;

    void display_val(BTnode *pt, ostream &os) const; //遍历函数;
    BTnode(const BTnode &);                          //节点构造函数;
    BTnode &operator=(const BTnode &);               //重载赋值运算符(有指针成员);
};

template <typename valType>
inline BTnode<valType>::BTnode(const valType &val) : _val(val), _cnt(1), _lchild(nullptr), _rchild(nullptr) {}

template <typename valType>
void BTnode<valType>::insert_value(const valType &val) //添加元素;
{
    if (val == _val) //元素存在, 次数加1;
    {
        _cnt++;
        (*BinaryTree<valType>::os()) << "BTnode::insert_value: increment count( " << val << " : " << _cnt << " )\n";
    }
    else if (val < _val)
    {
        if (nullptr == _lchild) //添加至左子树;
        {
            _lchild = new BTnode(val);
            (*BinaryTree<valType>::os()) << "ok: BTnode::insert_value at left child( " << val << " )\n";
        }
        else
        {
            _lchild->insert_value(val);
        }
    }
    else
    {
        if (nullptr == _rchild) //添加至右子树;
        {
            _rchild = new BTnode(val);
            (*BinaryTree<valType>::os()) << "ok: BTnode::insert_value at right child( " << val << " )\n";
        }
        else
        {
            _rchild->insert_value(val);
        }
    }
    return;
}

template <typename valType>
bool BTnode<valType>::find_value(const valType &val) const
{
    if (val == _val)
    {
        return true;
    }
    if (val < _val)
    {
        return _lchild == nullptr ? false : _lchild->find_value(val); //在左子树中查找;
    }
    return _rchild == nullptr ? false : _rchild->find_value(val); //在右子树中查找;
}

template <typename valType>
void BTnode<valType>::lchild_leaf(BTnode *leaf, BTnode *subtree)
{
    while (subtree->_lchild != nullptr)
    {
        subtree = subtree->_lchild; //找到最左子树;
    }
    subtree->_lchild = leaf; //leaf节点添加至其中;
    return;
}

template <typename valType>
void BTnode<valType>::remove_value(const valType &val, BTnode *&prev)
{
    if (val < _val)
    {
        if (nullptr == _lchild)
        {
            return;
        }
        else
        {
            _lchild->remove_value(val, _lchild); //左子树中寻找;
        }
    }
    else if (val > _val)
    {
        if (nullptr == _rchild)
        {
            return;
        }
        else
        {
            _rchild->remove_value(val, _rchild); //右子树中寻找;
        }
    }
    else //找到需删除的元素;
    {
        //重置此树并删除此节点;
        if (_rchild != nullptr)
        {
            prev = _rchild;
            if (_lchild != nullptr)
            {
                if (!prev->_lchild)
                {
                    prev->_lchild = _lchild;
                }
                else
                {
                    BTnode<valType>::lchild_leaf(_lchild, prev->_lchild);
                }
            }
        }
        else
        {
            prev = _lchild;
        }
        delete this;
    }
    return;
}

template <typename valType>
inline void BTnode<valType>::display_val(BTnode *pt, ostream &os) const
{
    os << pt->_val;
    if (pt->_cnt > 1)
    {
        os << "( " << pt->_cnt << " ) ";
    }
    else
    {
        os << ' ';
    }
    return;
}

template <typename valType>
void BTnode<valType>::preorder(BTnode *pt, ostream &os) const
{
    if (pt != nullptr)
    {
        display_val(pt, os);
        preorder(pt->_lchild, os);
        preorder(pt->_rchild, os);
    }
    return;
}

template <typename valType>
void BTnode<valType>::inorder(BTnode *pt, ostream &os) const
{
    if (pt != nullptr)
    {
        inorder(pt->_lchild, os);
        display_val(pt, os);
        inorder(pt->_rchild, os);
    }
    return;
}

template <typename valType>
void BTnode<valType>::postorder(BTnode *pt, ostream &os) const
{
    if (pt != nullptr)
    {
        postorder(pt->_lchild, os);
        postorder(pt->_rchild, os);
        display_val(pt, os);
    }
    return;
}

template <typename elemType>
class BinaryTree
{
public:
    BinaryTree() : _root(nullptr) {}
    BinaryTree(const vector<elemType> &);
    BinaryTree(const BinaryTree &rhs) { copy(_root, rhs._root); }
    ~BinaryTree() { clear(); }
    BinaryTree &operator=(const BinaryTree &);

    void insert(const vector<elemType> &);
    void insert(const elemType &);
    void remove(const elemType &);
    void clear() { clear(_root), _root = nullptr; }
    bool empty() { return _root == nullptr; }
    void inorder(ostream &os = *_current_os) const { _root->inorder(_root, os); }
    void postorder(ostream &os = *_current_os) const { _root->postorder(_root, os); }
    void preorder(ostream &os = *_current_os) const { _root->preorder(_root, os); }
    bool find(const elemType &) const;
    ostream &print(ostream &os = *_current_os,
                   void (BinaryTree<elemType>::*traversal)(ostream &) const = &BinaryTree<elemType>::inorder) const;
    static void current_os(ostream *os)
    {
        if (os != nullptr)
        {
            _current_os = os;
        }
        return;
    }
    static ostream *os() { return _current_os; }

private:
    BTnode<elemType> *_root;
    static ostream *_current_os;

    void copy(BTnode<elemType> *&tar, BTnode<elemType> *src);
    void clear(BTnode<elemType> *);
    void remove_root();
};

template <typename elemType>
ostream *BinaryTree<elemType>::_current_os = &cout;

template <typename elemType>
inline BinaryTree<elemType> &BinaryTree<elemType>::operator=(const BinaryTree &rhs)
{
    if (this != &rhs)
    {
        clear();
        copy(_root, rhs._root);
    }
    return _root;
}

template <typename elemType>
inline void BinaryTree<elemType>::insert(const elemType &elem)
{
    if (nullptr == _root)
    {
        (*BinaryTree<elemType>::os()) << "BinaryTree::insert: root( " << elem << " )\n";
        _root = new BTnode<elemType>(elem);
    }
    else
    {
        _root->insert_value(elem);
    }
    return;
}

template <typename elemType>
BinaryTree<elemType>::BinaryTree(const vector<elemType> &vec)
{
    _root = nullptr;
    insert(vec);
}

template <typename elemType>
void BinaryTree<elemType>::insert(const vector<elemType> &vec)
{
    for (int ix = 0; ix < vec.size(); ++ix)
    {
        insert(vec[ix]);
    }
    return;
}

template <typename elemType>
inline void BinaryTree<elemType>::remove(const elemType &elem)
{
    if (_root != nullptr)
    {
        if (_root->_val == elem) //如果是根就删除根节点;
        {
            remove_root();
        }
        else
        {
            _root->remove_value(elem, _root);
        }
    }
    return;
}

template <typename elemType>
void BinaryTree<elemType>::remove_root()
{
    if (nullptr == _root)
    {
        return;
    }
    BTnode<elemType> *tmp = _root;
    if (_root->_rchild != nullptr)
    {
        //将左子节点移动到右子节点左子树的底部, 满足二叉查找树的性质;
        _root = _root->_rchild;
        BTnode<elemType> *lc = tmp->_lchild;
        BTnode<elemType> *newlc = _root->_lchild;
        if (lc != nullptr)
        {
            if (nullptr == newlc)
            {
                //没有左子树则直接连接到此左指针即可;
                _root->_lchild = lc;
            }
            else
            {
                //否则在newlc子树中找到最左子树并把lc子树连接上;
                BTnode<elemType>::lchild_leaf(lc, newlc);
            }
        }
    }
    else //没有右子树则把左子树直接作为根节点;
    {
        _root = _root->_lchild;
    }
    delete tmp;
    return;
}

template <typename elemType>
void BinaryTree<elemType>::clear(BTnode<elemType> *pt)
{
    if (pt != nullptr)
    {
        clear(pt->_lchild);
        clear(pt->_rchild);
        delete pt;
    }
    return;
}

template <typename elemType>
ostream &BinaryTree<elemType>::print(ostream &os, void (BinaryTree::*traversal)(ostream &) const) const
{
    (this->*traversal)(os);
    return os;
}

template <typename elemType>
inline ostream &operator<<(ostream &os, const BinaryTree<elemType> &bt)
{
    os << "Tree: " << endl;
    bt.print(os, &BinaryTree<elemType>::inorder); //重载<<运算符使用中序遍历作为默认输出;
    return os;
}

template <typename elemType>
inline bool BinaryTree<elemType>::find(const elemType &elem) const
{
    return nullptr == _root ? false : _root->find_value(elem); //查找元素;
}

template <typename elemType>
void BinaryTree<elemType>::copy(BTnode<elemType> *&tar, BTnode<elemType> *src)
{
    //把src复制给tar;
    if (src != nullptr)
    {
        tar = new BTnode<elemType>(src->_val);
        copy(tar->_lchild, src->_lchild);
        copy(tar->_rchild, src->_rchild);
    }
    return;
}

int main()
{
    ofstream log("logfile.txt");
    if (!log)
    {
        cerr << "error: unable to open file!\n";
        return -1;
    }
    else
    {
        BinaryTree<string>::current_os(&log);
    }
    BinaryTree<string> bt;
    bt.insert("Piglet");
    bt.insert("Eeyore");
    bt.insert("Roo");
    bt.insert("Tigger");
    bt.insert("Chris");
    bt.insert("Pooh");
    bt.insert("Kanga");

    log << "preorder traversal: \n";
    bt.preorder(log);

    log << "\nabout to remove root: Piglet\n";
    bt.remove("Piglet");

    log << "\npreorder traversal after Piglet removal: \n";
    bt.preorder(log);

    log << "\nabout to remove Eeyore\n";
    bt.remove("Eeyore");

    log << "\npreorder traversal after Piglet removal: \n";
    bt.preorder(log);

    log << "\ninorder traversal:\n";
    bt.inorder(log);

    log << "\npostorder traversal:\n";
    bt.postorder(log);

    int ia[14] = {24, 18, 36, 12, 14, 8, 24, 1, 42, 24, 8, 8, 16, 55};
    vector<int> ivec(ia, ia + 14);
    BinaryTree<int> bt2(ivec);

    cout << "preorder traversal:\n";
    cout << "should see\n";
    bt2.preorder();

    bt2.clear();
    cout << "\nbt is now " << (bt2.empty() ? " empty! " : " oops -- not empty!") << endl;

    sort(ivec.begin(), ivec.end());
    bt2.insert(ivec);

    cout << "inorder traversal:\n";
    bt2.inorder();

    bt2.insert(ivec);

    cout << "postorder traversal:\n";
    bt2.postorder();

    return 0;
}

//------------------------------------------------------------

//Practise6.2.cpp

#include <iostream>
#include <fstream>
using namespace std;

template <typename elemType>
class Matrix
{
private:
    friend Matrix<elemType>
    operator+(const Matrix<elemType> &m1, const Matrix<elemType> &m2)
    {
        Matrix<elemType> result(m1);
        result += m2;
        return result;
    }
    friend Matrix<elemType>
    operator*(const Matrix<elemType> &m1, const Matrix<elemType> &m2)
    {
        Matrix<elemType> result(m1);
        result *= m2;
        return result;
    }

public:
    Matrix(int rows, int columns);
    Matrix(const Matrix &m);
    ~Matrix() { delete[] _matrix; }
    Matrix &operator=(const Matrix &m);
    void operator+=(const Matrix &m);
    void operator*=(const Matrix &m);
    elemType &operator()(int row, int column) { return _matrix[row * cols() + column]; }
    const elemType &operator()(int row, int column) const { return _matrix[row * cols() + column]; }
    int rows() const { return _rows; }
    int cols() const { return _cols; }
    ostream &print(ostream &os) const;

protected:
    int _rows;
    int _cols;
    elemType *_matrix;
};

template <typename elemType>
Matrix<elemType>::Matrix(int rows, int columns) : _rows(rows), _cols(columns)
{
    int size = _rows * _cols;
    _matrix = new elemType[size];
    for (int ix = 0; ix < size; ++ix)
    {
        _matrix[ix] = elemType();
    }
}

template <typename elemType>
Matrix<elemType>::Matrix(const Matrix &m)
{
    _rows = m._rows;
    _cols = m._cols;
    int mat_size = _rows * _cols;
    _matrix = new elemType[mat_size];
    for (int ix = 0; ix < mat_size; ++ix)
    {
        _matrix[ix] = m._matrix[ix];
    }
}

template <typename elemType>
Matrix<elemType> &Matrix<elemType>::operator=(const Matrix &m)
{
    if (this != &m)
    {
        _rows = m._rows;
        _cols = m._cols;
        int mat_size = _rows * _cols;
        delete[] _matrix;
        _matrix = new elemType[mat_size];
        for (int ix = 0; ix < mat_size; ++ix)
        {
            _matrix[ix] = m._matrix[ix];
        }
    }
    return *this;
}

template <typename elemType>
void Matrix<elemType>::operator+=(const Matrix &m)
{
    int matrix_size = cols() * rows();
    for (int ix = 0; ix < matrix_size; ++ix)
    {
        _matrix[ix] += m._matrix[ix];
    }
    return;
}

template <typename elemType>
void Matrix<elemType>::operator*=(const Matrix &m)
{
    int matrix_size = cols() * rows();
    for (int ix = 0; ix < matrix_size; ++ix)
    {
        _matrix[ix] *= m._matrix[ix];
    }
    return;
}

template <typename elemType>
ostream &Matrix<elemType>::print(ostream &os) const
{
    int col = cols();
    int matrix_size = col * rows();

    for (int ix = 0; ix < matrix_size; ++ix)
    {
        os << _matrix[ix] << ' ';
        if (0 == (ix + 1) % col)
        {
            os << endl;
        }
    }
    os << endl;
    return os;
}

template <typename elemType>
inline ostream &operator<<(ostream &os, const Matrix<elemType> &m)
{
    return m.print(os);
}

int main()
{
    float ar[16] = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
                    0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f};
    float ar2[16] = {1.3f, 0.4f, 2.6f, 8.2f, 6.2f, 1.7f, 1.3f, 8.3f,
                     4.2f, 7.4f, 2.7f, 1.9f, 6.3f, 8.1f, 5.6f, 6.6f};
    ofstream log("logmatrix.txt");
    if (!log)
    {
        cerr << "can't open log file!\n";
        return -1;
    }
    Matrix<float> identity(4, 4);
    log << "identity:\n";
    log << identity << endl;

    for (int i = 0, k = 0; i < 4; ++i)
    {
        for (int j = 0; j < 4; ++j)
        {
            identity(i, j) = ar[k++];
        }
    }
    log << "identity after set:\n";
    log << identity << endl;

    Matrix<float> m(identity);
    log << "m: memberwise initialized:\n";
    log << m << endl;
    Matrix<float> m2(8, 12);
    log << "m2: 8x12:\n";
    log << m2 << endl;

    m2 = m;
    log << "m2 after memberwise assigned to m:\n";
    log << m2 << endl;

    Matrix<float> m3(4, 4);
    for (int ix = 0, kx = 0; ix < 4; ++ix)
    {
        for (int j = 0; j < 4; ++j)
        {
            m3(ix, j) = ar2[kx++];
        }
    }
    log << "m3: assigned random values:\n";
    log << m3 << endl;

    Matrix<float> m4 = m3 * identity;
    log << m4 << endl;

    Matrix<float> m5 = m3 + m4;
    log << m5 << endl;

    m3 += m4;
    log << m3 << endl;

    return 0;
}

//-----------------------------------------------------------;

//--------------------------------------------2021年8月1日 ----------------------------------------------------

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MZZDX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值