c++primer第十四章代码重用

news2024/10/9 13:01:11

包含对象成员的类

包含:私有部分有一个类。

使用模板类,声明对象时必须指定具体的数据类型。

范例:

#ifndef STUDENTC_H_
#define STUDENTC_H_
#include <iostream>
#include <string>
#include <valarray>
class Student
{
private:
    typedef std::valarray<double> ArrayDb;
    std::string name; // contained object
    ArrayDb scores;
    // contained object
    //  private method for scores output
    std::ostream &arr_out(std::ostream &os) const;

public:
    Student() : name("Null Student"), scores() {}
    Student(const std::string &s)
        : name(s), scores() {}
    explicit Student(int n) : name("Nully"), scores(n) {}
    Student(const std::string &s, int n)
        : name(s), scores(n) {}
    Student(const std::string &s, const ArrayDb &a)
        : name(s), scores(a) {}
    Student(const char *str, const double *pd, int n)
        : name(str), scores(pd, n) {}
    ~Student() {}

    double Average() const;
    const std::string &Name() const;
    double &operator[](int i);
    double operator[](int i) const;

    // friends
    // input
    friend std::istream &operator>>(std::istream &is, Student &stu);
    // 1 word
    friend std::istream &getline(std::istream &is, Student &stu); // output
    // 1 line
    friend std::ostream &operator<<(std::ostream &os, const Student &stu);
};
#endif

类定义中包括:

可以在代码中使用ArrayDb,而不是std::valarray <double>.

typedef放在类定义的私有部分意味着可以Student类的实现中使用它,但是在类外面不能使用。

其中的--个构造函数使用了关键字explicit:

在上述构造函数中,第一个参数表示数组的元素个数,而不是数组中的值,因此将一个构造函数用作int 到Student 的转换函数是没有意义的,所以使用 explicit 关闭隐式转换。如果省略该关键字,则可以编写如下所示的代码:

防止出现手误的错误。

初始化被包含的对象

对于继承的对象,构造函数在成员初始化列表中使用类名来调用特定的基类构造函数。对于成员对象构造函数则使用成员名。例如,最后一个构造函数:

因为该构造函数初始化的是成员对象,而不是继承的对象,所以在初始化列表中使用的是成员名,而不是类名。初始化列表中的每一项都调用与之匹配的构造函数,即name(str)调用String(const   char*)构造函数,scores(pd,n)调用ArrayDb(const double*,int)构造函数。

使用被包含对象的接口

被包含的对象的接口不是公有的,但是可以在类方法中使用。

上述代码定义了可由 Student对象调用的方法,该方法内部使用了valarray的方法size()和sumO)。这是因为 scores是一个 valarray 对象,所以它可以调用 valarray 类的成员函数。简而言之,Student对象调用Student 的方法,而后者使用被包含的valarray 对象来调用 valarray 类的方法。

同样可以定义友元函数:

注意,operator<<(ostream& os,const Student&stu)必须是 Student 类的友元函数,这样才能访问 name 成员。另一种方法是,在该函数中使用公有方法Name(),而不是私有数据成员name。

因为 stu.name 是一个 string对象,所以它将调用 operatot<<(ostream&,conststring&)函数,该函数位于 string 类中。

#include "studentc.h"
using std::endl;
using std::istream;
using std::ostream;
using std::string;

// public methods
double Student::Average() const
{
    if (scores.size() > 0)
        return scores.sum() / scores.size();
    else
        return 0;
}

const string &Student::Name() const
{
    return name;
}

double &Student::operator[](int i)
{
    return scores[i];
}

double Student::operator[](int i) const
{
    return scores[i];
}

// private method
ostream &Student::arr_out(ostream &os) const
{
    int i;
    int lim = scores.size();
    if (lim > 0)
    {
        for (i = 0; i < lim; i++)
        {
            os << scores[i] << " ";
            if (i % 5 == 4)
                os << endl;
        }

        if (i % 5 != 0)
            os << endl;
    }

    else
        os << "empty array ";
    return os;
}

// friend
// use string version of operator>>()
istream &operator>>(istream &is, Student &stu)
{
    is >> stu.name;
    return is;
}

// use string friend getline(ostream &,const string &)
istream &getline(istream &is, Student &stu)
{
    getline(is, stu.name);
    return is;
}

// use string version of operator<<()
ostream &operator<<(ostream &os, const Student &stu)
{
    os << "Scores for " << stu.name << ":\n";
    stu.arr_out(os); // use private method for scores
    return os;
}

使用新的student类

#include <iostream>
#include "studentc.h"
using std::cin;
using std::cout;
using std::endl;
void set(Student &sa, int n);
const int pupils = 3;
const int quizzes = 5;

int main()
{
    Student ada[pupils] = {Student(quizzes), Student(quizzes), Student(quizzes)};
    int i;
    for (i = 0; i < pupils; ++i)
        set(ada[i], quizzes);
    cout << "\nStudent ist:\n";
    for (i = 0; i < pupils; ++i)
        cout << ada[i].Name() << endl;
    cout << "\nResults:";
    for (i = 0; i < pupils; ++i)
    {
        cout << endl
             << ada[i];
        cout << "average: " << ada[i].Average() << endl;
    }

    cout << "Done.\n";
    return 0;
}
void set(Student &sa, int n)
{
    cout << "Please enter the student's name:";
    getline(cin, sa);
    cout << "Please enter " << n << " quiz scores:\n";
    for (int i = 0; i < n; i++)
        cin >> sa[i];
    while (cin.get() != '\n')
        continue;
}

运行结果

私有继承

私有继承是has-a关系

公有继承是is-a关系

私有继承关键词private

区别是省略了显式对象名称,直接在内联函数中使用了类名。

对于对象中的对象

私有继承可以使用类名和作用域解析符来调用基类的方法。

使用强制类型转换访问基类对象

#ifndef STUDENTI_H_
#define STUDENTI_H_
#include <iostream>
#include <string>
#include <valarray>
class Student : private std::string, private std::valarray<double>
{
private:
    typedef std::valarray<double> ArrayDb;

    // contained object
    //  private method for scores output
    std::ostream &arr_out(std::ostream &os) const;

public:
    Student() : std::string("Null Student"), ArrayDb() {}
    Student(const std::string &s)
        : std::string(s), ArrayDb() {}
    explicit Student(int n) : std::string("Nully"), ArrayDb(n) {}
    Student(const std::string &s, int n)
        : std::string(s), ArrayDb(n) {}
    Student(const std::string &s, const ArrayDb &a)
        : std::string(s), ArrayDb(a) {}
    Student(const char *str, const double *pd, int n)
        : std::string(str), ArrayDb(pd, n) {}
    ~Student() {}

    double Average() const;
    const std::string &Name() const;
    double &operator[](int i);
    double operator[](int i) const;

    // friends
    // input
    friend std::istream &operator>>(std::istream &is, Student &stu);
    // 1 word
    friend std::istream &getline(std::istream &is, Student &stu); // output
    // 1 line
    friend std::ostream &operator<<(std::ostream &os, const Student &stu);
};
#endif
#include "studenti.h"
using std::endl;
using std::istream;
using std::ostream;
using std::string;

// public methods
double Student::Average() const
{
    if (ArrayDb::size() > 0)
        return ArrayDb::sum() / ArrayDb::size();
    else
        return 0;
}

const string &Student::Name() const
{
    return (const string &)*this;
}

double &Student::operator[](int i)
{
    return ArrayDb::operator[](i);
}

double Student::operator[](int i) const
{
    return ArrayDb::operator[](i);
}

// private method
ostream &Student::arr_out(ostream &os) const
{
    int i;
    int lim = ArrayDb::size();
    if (lim > 0)
    {
        for (i = 0; i < lim; i++)
        {
            os << ArrayDb::operator[](i) << " ";
            if (i % 5 == 4)
                os << endl;
        }

        if (i % 5 != 0)
            os << endl;
    }

    else
        os << "empty array ";
    return os;
}

// friend
// use string version of operator>>()
istream &operator>>(istream &is, Student &stu)
{
    is >> (string &)stu;
    return is;
}

// use string friend getline(ostream &,const string &)
istream &getline(istream &is, Student &stu)
{
    getline(is, (string &)stu);
    return is;
}

// use string version of operator<<()
ostream &operator<<(ostream &os, const Student &stu)
{
    os << "ArrayDb::operator[] for " << (const string &)stu << ":\n";
    stu.arr_out(os); // use private method for ArrayDb::operator[]
    return os;
}

同样的程序文件

#include <iostream>
#include "studenti.h"
using std::cin;
using std::cout;
using std::endl;
void set(Student &sa, int n);
const int pupils = 3;
const int quizzes = 5;

int main()
{
    Student ada[pupils] = {Student(quizzes), Student(quizzes), Student(quizzes)};
    int i;
    for (i = 0; i < pupils; ++i)
        set(ada[i], quizzes);
    cout << "\nStudent ist:\n";
    for (i = 0; i < pupils; ++i)
        cout << ada[i].Name() << endl;
    cout << "\nResults:";
    for (i = 0; i < pupils; ++i)
    {
        cout << endl
             << ada[i];
        cout << "average: " << ada[i].Average() << endl;
    }

    cout << "Done.\n";
    return 0;
}
void set(Student &sa, int n)
{
    cout << "Please enter the student's name:";
    getline(cin, sa);
    cout << "Please enter " << n << " quiz scores:\n";
    for (int i = 0; i < n; i++)
        cin >> sa[i];
    while (cin.get() != '\n')
        continue;
}

结果和之前相同

包含和私有继承的选择

 多重继承

多重继承(MI)表示有多个直接基类的类。

MI的问题:

从两个不同的基类继承同名方法:

#ifndef WORKER0_H_
#define WORKER0_H_
#include <string>

class Worker // an abstract base class
{
private:
    std::string fullname;
    long id;

public:
    Worker() : fullname("no one"), id(0L) {}
    Worker(const std::string &s, long n) : fullname(s), id(n) {}
    virtual ~Worker() = 0; // pure virtual destructor
    virtual void Set();
    virtual void Show() const;
};

class Waiter : public Worker
{
private:
    int panache;

public:
    Waiter() : Worker(), panache(0) {}
    Waiter(const std::string &s, long n, int p = 0)
        : Worker(s, n), panache(p) {}
    Waiter(const Worker &wk, int p = 0) : Worker(wk), panache(p) {}
    void Set();
    void Show() const;
};
class Singer : public Worker
{
protected:
    enum
    {
        other,
        alto,
        contralto,
        soprano,
        bass,
        baritone,
        tenor
    };
    enum
    {
        Vtypes = 7
    };

private:
    static char *pv[Vtypes];
    int voice;

public:
    Singer() : Worker(), voice(other) {}
    Singer(const std::string &s, long n, int v = other)
        : Worker(s, n), voice(v) {}
    Singer(const Worker &wk, int v = other)
        : Worker(wk), voice(v) {}
    void Set();
    void Show() const;
};
#endif
#include "worker0.h"
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
// Worker methods

// must implement virtual destructor,even if pure
Worker::~Worker() {}
void Worker::Set()
{
    cout << "Enter worker's name: ";
    getline(cin, fullname);
    cout << "Enter worker's ID:";
    cin >> id;
    while (cin.get() != '\n')
        continue;
}
void Worker::Show() const
{
    cout << "Name:" << fullname << "\n";
    cout << "Employee ID:" << id << "\n";
}

// Waiter methods
void Waiter::Set()
{
    Worker::Set();
    cout << "Enter waiter's panache rating:";
    cin >> panache;
    while (cin.get() != '\n')
        continue;
}
void Waiter::Show() const
{
    cout << "Category: waiter\n";
    Worker::Show();
    cout << "Panache rating:" << panache << "\n";
}

// Singer methods
char *Singer::pv[] = {"other", "alto", "contralto",
                      "soprano", "bass", "baritone", "tenor"};
void Singer::Set()
{
    Worker::Set();
    cout << "Enter number for singer's vocal range:\n";
    int i;
    for (i = 0; i < Vtypes; i++)
    {
        cout << i << ":" << pv[i] << "  ";
        if (i % 4 == 3)
            cout << endl;
    }

    if (i % 4 != 0)
        cout << endl;
    cin >> voice;
    while (cin.get() != '\n')
        continue;
}

void Singer::Show() const
{
    cout << "Category: singer \n";
    Worker::Show();
    cout << "Vocal range: " << pv[voice] << endl;
}

#include <iostream>
#include "worker0.h"
const int LIM = 4;
int main()
{
    Waiter bob("Bob Apple", 314L, 5);
    Singer bev("Beverly Hills", 522L, 3);
    Waiter w_temp;

    Singer s_temp;
    Worker *pw[LIM] = {&bob, &bev, &w_temp, &s_temp};
    int i;
    for (i = 2; i < LIM; i++)
        pw[i]->Set();
    for (i = 0; i < LIM; i++)
    {
        pw[i]->Show();
        std::cout << std::endl;
    }

    return 0;
}

在祖先相同时,使用 MI必须引入虚基类,并修改构造函数初始化列表的规则。

修改后的代码

#ifndef WORKERMI_H_
#define WORKERMI_H_
#include <string>

class Worker // an abstract base class
{
private:
    std::string fullname;
    long id;

protected:
    virtual void Data() const;
    virtual void Get();

public:
    Worker() : fullname("no one"), id(0L) {}
    Worker(const std::string &s, long n) : fullname(s), id(n) {}
    virtual ~Worker() = 0; // pure virtual destructor
    virtual void Set();
    virtual void Show() const;
};

class Waiter : virtual public Worker
{
private:
    int panache;

protected:
    virtual void Data() const;
    virtual void Get();

public:
    Waiter() : Worker(), panache(0) {}
    Waiter(const std::string &s, long n, int p = 0)
        : Worker(s, n), panache(p) {}
    Waiter(const Worker &wk, int p = 0) : Worker(wk), panache(p) {}
    void Set();
    void Show() const;
};
class Singer : virtual public Worker
{
protected:
    enum
    {
        other,
        alto,
        contralto,
        soprano,
        bass,
        baritone,
        tenor
    };
    enum
    {
        Vtypes = 7
    };

protected:
    virtual void Data() const;
    virtual void Get();

private:
    static char *pv[Vtypes];
    int voice;

public:
    Singer() : Worker(), voice(other) {}
    Singer(const std::string &s, long n, int v = other)
        : Worker(s, n), voice(v) {}
    Singer(const Worker &wk, int v = other)
        : Worker(wk), voice(v) {}
    void Set();
    void Show() const;
};

// multiple inheritance
class SingingWaiter : public Singer, public Waiter
{
protected:
    void Data() const;
    void Get();

public:
    SingingWaiter() {};
    SingingWaiter(const std::string &s, long n, int p = 0,
                  int v = other) : Worker(s, n), Waiter(s, n, p), Singer(s, n, v) {}
    SingingWaiter(const Worker &wk, int p = 0, int v = other)
        : Worker(wk), Waiter(wk, p), Singer(wk, v) {}
    SingingWaiter(const Waiter &wt, int v = other)
        : Worker(wt), Waiter(wt), Singer(wt, v) {}
    SingingWaiter(const Singer &wt, int p = 0)
        : Worker(wt), Waiter(wt, p), Singer(wt) {}
    void Set();
    void Show() const;
};

#endif

#include "workermi.h"
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
// Worker methods

// must implement virtual destructor,even if pure
Worker::~Worker() {}

// protect methods
void Worker::Data() const
{
    cout << "Name: " << fullname << endl;
    cout << "Employee ID:" << id << endl;
}

void Worker::Get()
{
    getline(cin, fullname);
    cout << "Enter worker's ID:";
    cin >> id;
    while ((cin.get() != '\n'))
    {
        continue;
    }
}
void Worker::Set()
{
    cout << "Enter worker's name: ";
    getline(cin, fullname);
    Worker::Get();
    Get();
}
void Worker::Show() const
{
    cout << "Category: waiter\n";
    Worker::Data();
    Data();
}
// Waiter methods
void Waiter::Set()
{
    cout << "Enter waiter's name:";
    Worker::Get();
    Get();
}

void Waiter::Show() const
{
    cout << "Category: waiter\n";
    Worker::Data();
    Data();
}

// protect method
void Waiter::Data() const
{
    cout << "Panache rating:" << panache << endl;
}

void Waiter::Get()
{
    cout << "Enter waiter's panache rating:";
    cin >> panache;
    while (cin.get() != '\n')
        continue;
}

// Singer methods
char *Singer::pv[] = {"other", "alto", "contralto",
                      "soprano", "bass", "baritone", "tenor"};
void Singer::Set()
{
    cout << "Enter singer's name\n";
    Worker::Get();
    Get();
}

void Singer::Show() const
{
    cout << "Category: singer \n";
    Worker::Data();
    Data();
}

// protect methods
void Singer::Data() const
{
    cout << "Vocal range: " << pv[voice] << endl;
}

void Singer::Get()
{

    cout << "Enter number for singer's vocal range:\n";
    int i;
    for (i = 0; i < Vtypes; i++)
    {
        cout << i << ":" << pv[i] << "  ";
        if (i % 4 == 3)
            cout << endl;
    }

    if (i % 4 != 0)
        cout << endl;
    cin >> voice;
    while (cin.get() != '\n')
        continue;
}

// SingerWaiter methods

void SingingWaiter::Data() const
{
    Singer::Data();
    Waiter::Data();
}

void SingingWaiter::Get()
{
    Waiter::Get();
    Singer::Get();
}

void SingingWaiter::Set()
{
    cout << "Enter singing waiter's name: ";
    Worker::Get();
    Get();
}

void SingingWaiter::Show() const
{
    cout << "Category:singing waiter\n";
    Worker::Data();
    Data();
}
#include <iostream>
#include <cstring>
#include "workermi.h"
const int SIZE = 5;
int main()
{
    using std::cin;
    using std::cout;
    using std::endl;
    using std::strchr;
    Worker *lolas[SIZE];
    int ct;
    for (ct = 0; ct < SIZE; ct++)
    {
        char choice;
        cout << "Enter the employee category:\n"
             << "w:waiter s:singer "
             << "t:singing waiter q: quit\n";
        cin >> choice;
        while (strchr("wstq", choice) == NULL)
        {
            cout << "Please enteraw,s,t,or g:";
            cin >> choice;
        }

        if (choice == 'q')
            break;
        switch (choice)
        {
        case 'w':
            lolas[ct] = new Waiter;
            break;
        case 's':
            lolas[ct] = new Singer;
            break;
        case 't':
            lolas[ct] = new SingingWaiter;
            break;
        }

        cin.get();
        lolas[ct]->Set();
    }

    cout << "\nHere is your staff:\n";
    int i;
    for (i = 0; i < ct; i++)
    {
        cout << endl;
        lolas[i]->Show();
    }

    for (i = 0; i < ct; i++)
        delete lolas[i];
    cout << "Bye.\n";
    return 0;
}

类模板 

定义类模板

对于下列类

模板类的开头

template <class Type>

同时将所有的Typedef 改成Type

#ifndef STACKTP_H_
#define STACKTP_H_

template <class Type>
class Stack
{
private:
    enum
    {
        MAX = 10
    };               // constant specific to class
    Type items[MAX]; // holds stack items
    int top;
    // index for top stack item
public:
    Stack();
    bool isempty();
    bool isfull();
    bool push(const Type &item); // add item to stack
    bool pop(Type &item);
};
// pop top into item
template <class Type>
Stack<Type>::Stack()
{
    top = 0;
}
template <class Type>
bool Stack<Type>::isempty()
{
    return top == 0;
}
template <class Type>
bool Stack<Type>::isfull()
{
    return top == MAX;
}
template <class Type>
bool Stack<Type>::push(const Type &item)
{
    if (top < MAX)
    {
        items[top++] = item;
        return true;
    }
    else
        return false;
}
template <class Type>
bool Stack<Type>::pop(Type &item)
{
    if (top > 0)
    {
        item = items[--top];
        return true;
    }
    else
        return false;
}

#endif

使用模板类

#include <iostream>
#include <string>
#include <cctype>
#include "stacktp.h"
using std::cin;
using std::cout;
int main()
{

    Stack<std::string> st;
    char ch;
    std::string po;
    cout << "Please enter A to add a purchase order, \n"
         << "P to process a PO, or Q to quit.\n";
    while (cin >> ch && toupper(ch) != 'Q')
    {
        while (cin.get() != '\n')
            continue;
        if (!isalpha(ch))
        {
            cout << '\a';
            continue;
        }
        switch (ch)
        {
        case 'A':
        case 'a':
            cout << "Enter a PO number to add: ";
            cin >> po;
            if (st.isfull())
                cout << "stack already full\n";
            else
                st.push(po);
            break;
        case 'P':
        case 'p':
            if (st.isempty())
                cout << "stack already empty\n";
            else
            {
                st.pop(po);
                cout << "PO #" << po << " popped\n";
            }
            break;
        }
        cout << "Please enter A to add a purchase order, \n"
             << "P to process a PO, or Q to quit. \n";
    }
    cout << "Bye\n";
    return 0;
}

只在程序中包含模板不能生成模板类,必须要求实例化。

指针堆栈

使用指针堆栈需要使用指针数组不然容易出错。

#ifndef STACKTP1_H_
#define STACKTP1_H_

template <class Type>
class Stack
{
private:
    enum
    {
        SIZE = 10
    };
    int stacksize;
    // constant specific to class
    Type *items; // holds stack items
    int top;
    // index for top stack item
public:
    explicit Stack(int ss = SIZE);
    Stack(const Stack &st);
    ~Stack() { delete[] items; }
    bool isempty() { return top == 0; }
    bool isfull() { return top == stacksize; }
    bool push(const Type &item); // add item to stack
    bool pop(Type &item);
    Stack &operator=(const Stack &st);
};

template <class Type>
Stack<Type>::Stack(int ss) : stacksize(ss), top(0)
{
    items = new Type[stacksize];
}

template <class Type>
Stack<Type>::Stack(const Stack &st)
{
    stacksize = st.stacksize;
    top = st.top;
    items = new Type[stacksize];
    for (int i = 0; i < top; i++)
        items[i] = st.items[i];
}
template <class Type>
bool Stack<Type>::push(const Type &item)
{
    if (top < stacksize)
    {
        items[top++] = item;
        return true;
    }
    else
        return false;
}
template <class Type>
bool Stack<Type>::pop(Type &item)
{
    if (top > 0)
    {
        item = items[--top];
        return true;
    }
    else
        return false;
}
template <class Type>
Stack<Type> &Stack<Type>::operator=(const Stack<Type> &st)
{
    if (this == &st)
        return *this;
    delete[] items;
    stacksize = st.stacksize;
    top = st.top;
    items = new Type[stacksize];
    for (int i = 0; i < top; i++)
        items[i] = st.items[i];
    return *this;
}
#endif
#include <iostream>
#include <cstdlib>
#include <ctime>
#include "stacktp1.h"
const int Num = 10;
int main()
{
    std::srand(std::time(0));
    // randomize rand()
    std::cout << "Please enter stack size:";
    int stacksize;
    std::cin >> stacksize;
    // create an empty stack with stacksize slots
    Stack<const char *> st(stacksize);
    // int basket
    const char *in[Num] = {
        "1:Hank Gilgamesh",
        "2:Kiki Ishtar",
        "3:Betty Rocker",
        "4:Ian Flagranti",
        "5:Wolfgang Kibble",
        "6: Portia Koop"
        "7:Joy Almondo",
        "8:Xaverie Paprika",
        "9:Juan Moore",
        "10:Misha Mache"}; // out basket
    const char *out[Num];
    int processed = 0;
    int nextin = 0;
    while (processed < Num)
    {
        if (st.isempty())
            st.push(in[nextin++]);
        else if (st.isfull())
            st.pop(out[processed++]); // 50-50 chance
        else if (std::rand() % 2 && nextin < Num)
            st.push(in[nextin++]);
        else
            st.pop(out[processed++]);
    }
    for (int i = 0; i < Num; i++)
        std::cout << out[i] << std::endl;
    std::cout << "Bye\n";
    return 0;
}

具有随机特性。

数组模板范例和非类型参数

首先介绍一个允许指定数组大小的简单数组模板。一种方法是在类中使用动态数组和构造函数参数来提供元素数目,最后一个版本的 Stack 模板采用这种方法。另一种方法是使用模板参数来提供常规数组的大小,

#ifndef ARRAYTP_H_
#define ARRAYTP_H_
#include <iostream>
#include <cstdlib>
template <class T, int n>
class ArrayTP
{
private:
    T ar[n];

public:
    ArrayTP() {};
    explicit ArrayTP(const T &v);
    virtual T &operator[](int i);
    virtual T operator[](int i) const;
};
template <class T, int n>
ArrayTP<T, n>::ArrayTP(const T &v)
{
    for (int i = 0; i < n; i++)
        ar[i] = v;
}

template <class T, int n>
T &ArrayTP<T, n>::operator[](int i)
{
    if (i < 0 || i >= n)
    {
        std::cerr << "Error in array limits:" << i
                  << " is out of range\n";
        std::exit(EXIT_FAILURE);
    }

    return ar[i];
}

template <class T, int n>
T ArrayTP<T, n>::operator[](int i) const
{
    if (i < 0 || i >= n)
    {
        std::cerr << "Error in array limits:" << i
                  << " is out of range\n";
        std::exit(EXIT_FAILURE);
    }

    return ar[i];
}

#endif

递归使用模板

#include <iostream>
#include "arraytp.h"
int main(void)
{
    using std::cout;
    using std::endl;
    ArrayTP<int, 10> sums;
    ArrayTP<double, 10> aves;
    ArrayTP<ArrayTP<int, 5>, 10> twodee;

    int i, j;

    for (i = 0; i < 10; i++)
    {
        sums[i] = 0;
        for (j = 0; j < 5; j++)
        {
            twodee[i][j] = (i + 1) * (j + 1);
            sums[i] += twodee[i][j];
        }

        aves[i] = (double)sums[i] / 10;
    }

    for (i = 0; i < 10; i++)
    {
        for (j = 0; j < 5; j++)
        {
            cout.width(2);
            cout << twodee[i][j] << ' ';
        }

        cout << ":sum =";
        cout.width(3);
        cout << sums[i] << ",average=" << aves[i] << endl;
    }

    cout << "Done.\n";
    return 0;
}

使用多个类型参数

// pairs.cpp--defining and using a Pair template
#include <iostream>
#include <string>
template <class T1, class T2>
class Pair
{
private:
    T1 a;
    T2 b;

public:
    T1 &first();
    T2 &second();
    T1 first() const { return a; }
    T2 second() const { return b; }
    Pair(const T1 &aval, const T2 &bval) : a(aval), b(bval) {}
    Pair() {}
};
template <class T1, class T2>
T1 &Pair<T1, T2>::first()
{
    return a;
}
template <class Tl, class T2>
T2 &Pair<Tl, T2>::second()
{
    return b;
}
int main()
{
    using std::cout;
    using std::endl;
    using std::string;
    Pair<string, int> ratings[4] =
        {Pair<string, int>("The Purple Duke", 5),
         Pair<string, int>("Jake's Frisco Al Fresco", 4),
         Pair<string, int>("Mont Souffle", 5),
         Pair<string, int>("Gertie's Eats", 3)};
    int joints = sizeof(ratings) / sizeof(Pair<string, int>);

    cout << "Rating:\t Eatery\n";
    for (int i = 0; i < joints; i++)
        cout << ratings[i].second() << ":\t"
             << ratings[i].first() << endl;
    cout << " Oops !Revised rating :\n ";
    ratings[3].first() = " Gertie's Fab Eat";
    ratings[3].second() = 6;
    cout << ratings[3].second() << ":\t"
         << ratings[3].first() << endl;
    return 0;
}

模板的具体化

隐式具体化

显式实例化

template class ArrayTP<string,100>;

显式具体化(待补充)

部分具体化(待补充)

成员模板

模板可用作结构,类或模板类的成员。

下列代码实现了模板的嵌套

#include <iostream>
using std::cout;
using std::endl;

template <typename T>
class beta
{
private:
    template <typename V> // nested template class member
    class hold
    {
    private:
        V val;

    public:
        hold(V v = 0) : val(v) {}
        void show() const { cout << val << endl; }
        V Value() const { return val; }
    };

    hold<T> q;
    hold<int> n;

public:
    // template object// template object
    beta(T t, int i) : q(t), n(i) {}
    template <typename U> // template method
    U blab(U u, T t)
    {
        return (n.Value() + q.Value()) * u / t;
    }
    void Show() const
    {
        q.show();
        n.show();
    }
};
int main()
{
    beta<double> guy(3.5, 3);
    guy.Show();
    cout << guy.blab(10, 2.3) << endl;
    cout << "Done\n";
    return 0;
}

更改代码

将模板用作参数

下列代码将模板作为参数

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

template <template <typename T> class Thing>
class Crab
{1.
private:
    Thing<int> s1;
    Thing<double> s2;

public:
    Crab() {};
    bool push(int a, double x) { return s1.push(a) && s2.push(x); }
    bool pop(int &a, double &x) { return s1.pop(a) && s2.pop(x); }
};

int main()
{
    using std::cin;
    using std::cout;
    using std::endl;
    Crab<Stack> nebula;
    int ni;
    double nb;

    cout << "Enter int double pairs, such as 4 3.5 (0 0 to be end): \n";
    while (cin >> ni >> nb && ni > 0 && nb > 0)
    {
        if (!nebula.push(ni, nb))
            break;
    }
    while (nebula.pop(ni, nb))
        cout << ni << ", " << nb << endl;
    cout << "Done.\n";
    return 0;
}

模板类和友元

模板的友元分类

  • 非模板友元
  • 约束模板友元,友元的类型取决于类被实例化的类型
  • 非约束模板友元,友元的所有具体化都是类的每一个具体化的友元。

 模板类的非模板友元函数

#include <iostream>
using std::cout;
using std::endl;
template <typename T>
class HasFriend
{
private:
    T item;
    static int ct;

public:
    HasFriend(const T &i) : item(i) { ct++; }
    ~HasFriend() { ct--; };
    friend void counts();
    friend void reports(HasFriend<T> &); // template parameter
};

// specialization
template <typename T>
int HasFriend<T>::ct = 0;

void counts()
{
    cout << "int count: " << HasFriend<int>::ct << "; ";
    cout << "double count: " << HasFriend<double>::ct << endl;
}

void reports(HasFriend<int> &hf)
{
    cout << "HasFriend<int>: " << hf.item << endl;
}
void reports(HasFriend<double> &hf)
{
    cout << "HasFriend<double>: " << hf.item << endl;
}

int main()
{
    cout << "No objects declared: ";
    counts();
    HasFriend<int> hfi1(10);
    cout << "After fhi1 declared: ";
    counts();
    HasFriend<int> hfi2(20);
    cout << "After fhi2 declared: ";
    counts();
    HasFriend<double> hfi3(10.5);
    cout << "After hfdb declared: ";
    counts();
    reports(hfi1);
    reports(hfi2);
    reports(hfi3);

    return 0;
}

程序说明:

定义了模板参数:

具体化:

 

模板类的约束模板友元函数

#include <iostream>
using std::cout;
using std::endl;
// template prototypes
template <typename T>
void counts();
template <typename T>
void reports(T &);

// template class
template <typename TT>
class HasFriendT
{
private:
    TT item;
    static int ct;

public:
    HasFriendT(const TT &i) : item(i) { ct++; }
    ~HasFriendT() { ct--; };
    friend void counts<TT>();
    friend void reports<>(HasFriendT<TT> &); // template parameter
};

// specialization
template <typename T>
int HasFriendT<T>::ct = 0;

template <typename T>
void counts()
{
    cout << "template size: " << sizeof(HasFriendT<T>) << "; ";
    cout << "template counts: " << HasFriendT<T>::ct << endl;
}

template <typename T>
void reports(T &hf)
{
    cout << hf.item << endl;
}

int main()
{

    counts<int>();
    HasFriendT<int> hfi1(10);
    HasFriendT<int> hfi2(20);
    HasFriendT<double> hfi3(10.5);
    reports(hfi1);
    reports(hfi2);
    reports(hfi3);
    cout << "counts<int>() output: \n";
    counts<int>();
    cout << "counts<double>() output: \n";
    counts<double>();

    return 0;
}

这种情况每个T类型都有自己的count函数

因为有定义:

模板类的非约束模板友元函数

约束模板友元函数是在类外面声明的模板的具体化。int 类具体化获得int 函数具体化,依此类推。通过在类内部声明模板,可以创建非约束友元函数,即每个函数具体化都是每个类具体化的友元。对于非约束友元,友元模板类型参数与模板类类型参数是不同的:

#include <iostream>
using std::cout;
using std::endl;

template <typename T>
class ManyFriend
{
private:
    T item;

public:
    ManyFriend(const T &i) : item(i) {}
    template <typename C, typename D>
    friend void show2(C &, D &);
};
template <typename C, typename D>
void show2(C &c, D &d)
{
    cout << c.item << ", " << d.item << endl;
}
int main()
{
    ManyFriend<int> hfi1(10);
    ManyFriend<int> hfi2(20);
    ManyFriend<double> hfdb(10.5);

    cout << "hfi1, hfi2: ";
    show2(hfi1, hfi2);
    cout << "hfdb, hfi2:";
    show2(hfdb, hfi2);

    return 0;
}

其中非约束模板友元函数为

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2199082.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Sceneform-EQR】(手势控制器实现)通过手势事件实现在AR/VR等三维场景中的控制模型旋转、平移与缩放

在Sceneform-EQR中实现旋转平移缩放手势 实现在AR/VR等三维场景&#xff0c;通过手势控制模型节点的缩放、平移和旋转。 实现思路 实现模型旋转 Sceneform-EQR(filament\opengl)中采用右手坐标系。通过欧拉角进行旋转采用Z->Y->X的顺序&#xff0c;在这里&#xff0c;…

dart-sass和node-sass的区别,使用dart-sass后可能会出现的问题

前言&#xff1a; 2020 年 10 月 27 日&#xff0c;Sass 官方团队正式宣布 Libsass 将弃用&#xff0c;以及基于它的 Node Sass 和 SassC&#xff0c;并且建议用户使用 Dart Sass。如果在 vue 脚手架搭建的项目中需要使用 sass&#xff0c;建议初始化时勾选 sass 配置&#xff…

MySQL数据库备份与恢复:全面指南

MySQL数据库备份与恢复&#xff1a;全面指南 MySQL数据库是许多应用程序的核心组件&#xff0c;数据的安全和可用性至关重要。无论是由于硬件故障、软件错误&#xff0c;还是人为操作失误&#xff0c;数据丢失的风险都时刻存在。因此&#xff0c;做好数据库的备份与恢复工作是…

前端无感刷新token机制(一文说明白)

前言 用户登录之后&#xff0c;会返回一个用户的标识&#xff0c;之后带上这个标识请求别的接口&#xff0c;就能识别出该用户。 标识登录状态的方案有两种&#xff1a; session 和 jwt。这两种方案一个服务端存储&#xff0c;通过 cookie 携带标识&#xff0c;一个在客户端存…

研发中台拆分之路:深度剖析、心得总结与经验分享

背景在 21 年&#xff0c;中台拆分在 21 年&#xff0c;以下为中台拆分的过程心得&#xff0c;带有一定的主观&#xff0c;偏向于中小团队中台建设参考&#xff08;这里的中小团队指 3-100 人的团队&#xff09;&#xff0c;对于大型团队不太适用&#xff0c;毕竟大型团队人中 …

leetcode C++特性 AIDL的一些细节

leetcode细节 C的一些特性 【C基础】std::move用法介绍-CSDN博客 c thread的join和joinable的区别_thread joinable-CSDN博客 C线程介绍_std::thread 头文件-CSDN博客 https://blog.csdn.net/weixin_46645965/article/details/136259902 【C】—— 观察者模式-CSDN博客 C 迭…

LabVIEW交直流接触器动态检测系统

LabVIEW软件与霍尔传感器技术结合的交直流接触器动态检测系统通过实时数据采集和处理技术&#xff0c;有效地测量并分析交直流接触器在吸合及吸持阶段的电流和电压变化&#xff0c;以及相应的功率消耗&#xff0c;从而优化电力和配电系统的性能和可靠性。 项目背景 交直流接触…

在供应商准入时,如何规避风险、提高效率?

在进行供应商准入时&#xff0c;进行风险审核是至关重要的步骤&#xff0c;它有助于确保供应链的稳定性和企业的长期成功。通过风险审核&#xff0c;企业可以确保供应商提供的产品或服务符合质量标准&#xff0c;同时评估供应商的财务稳健性&#xff0c;以降低供应链中断的风险…

电桥的作用是什么?

一、电桥的基本概念和原理 电桥是一种测量电阻、电容、电感等电学量的仪器&#xff0c;其原理基于电路中的克希荷夫定律以及欧姆定律。电桥由四个电阻分支组成&#xff0c;在精确测量电阻时&#xff0c;需要把待测电阻与一个已知电阻进行比较&#xff0c;通过调节电桥中的一个…

如何微调LLM大模型?看这一篇就够了!

在这篇文章中&#xff0c;我们将探讨一些用于策划高质量训练数据集的经验法则。 第一部分探讨了将LLM适应于领域数据的普遍方法第二部分讨论了咋确定微调是否适用于你的实际情况 1 介绍 微调LLMs是一门艺术与科学的结合&#xff0c;该领域的最佳实践仍在不断发展中。在本篇博…

【开源风云】从若依系列脚手架汲取编程之道(五)

&#x1f4d5;开源风云系列 &#x1f34a;本系列将从开源名将若依出发&#xff0c;探究优质开源项目脚手架汲取编程之道。 &#x1f349;从不分离版本开写到前后端分离版&#xff0c;再到微服务版本&#xff0c;乃至其中好玩的一系列增强Plus操作。 &#x1f348;希望你具备如下…

基于Java(Jsp+Sevlet)+MySql 实现的(Web)成绩管理系统

1 概述 1.1 开发背景 随着学生数量的日渐增多&#xff0c;学生教务系统的数据量也不断增加&#xff0c;这无疑大大增加了教务系统的负担。如果能把负责学生成绩管理的模块独立出来形成一个独立的系统&#xff0c;便可以有效降低教务系统的数据量&#xff0c;不仅可以方便管理…

封装el-upload组件,用于上传图片和视频的组件

使用环境 vue3element plus 需要根据后端返回结构修改的函数&#xff1a;onPreview onRemove onSuccess 组件使用 基本使用 源代码&#xff1a; <script setup> import AutoUploadFile from /components/auto-upload-file/index.vue function change(urls){console.log…

懂球短视频系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;上传视频管理&#xff0c;用户管理&#xff0c;懂球视频管理&#xff0c;分享视频管理&#xff0c;收藏视频管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;上传视频&a…

深入剖析递归算法:原理、特点、应用与优化策略

在上一篇文章&#x1f449;【剖析十大经典二叉树题目】中&#xff0c;运用到了大量的递归算法&#xff0c;故本文将解析递归算法。 目录 &#x1f4af;引言 &#x1f4af;递归算法的定义与原理 ⭐定义 ⭐原理 &#x1f4af;递归算法的特点 ⭐简洁性 ⭐可读性 ⭐通用性 …

【Java】单例模式详解与实践

欢迎浏览高耳机的博客 希望我们彼此都有更好的收获 感谢三连支持&#xff01; 单例模式 Singleton是一种常用的软件模式&#xff0c;确保一个类只有一个实例&#xff0c;并提供一个全局访问方法来获取这个实例。这种模式广泛应用于需要控制实例化次数的场景&#xff0c;如数据库…

昇思MindSpore进阶教程--数据处理性能优化(中)

大家好&#xff0c;我是刘明&#xff0c;明志科技创始人&#xff0c;华为昇思MindSpore布道师。 技术上主攻前端开发、鸿蒙开发和AI算法研究。 努力为大家带来持续的技术分享&#xff0c;如果你也喜欢我的文章&#xff0c;就点个关注吧 shuffle性能优化 shuffle操作主要是对有…

VMware ESXi 8.0U3 集成 AQC 网卡定制版更新 OEM BIOS 2.7 支持 Windows Server 2025

VMware ESXi 8.0U3 集成 AQC 网卡定制版更新 OEM BIOS 2.7 支持 Windows Server 2025 VMware ESXi 8.0U3 macOS Unlocker & OEM BIOS 集成网卡驱动和 NVMe 驱动 (集成驱动版) 发布 ESXi 8.0U3 集成驱动版&#xff0c;在个人电脑上运行企业级工作负载 请访问原文链接&…

数字化转型引领新时代:从架构到产品的全链路创新解析

在当前瞬息万变的商业环境中&#xff0c;数字化转型已经成为各类组织的核心战略手段。本文从数字化专业知识体系 (DPBOK) 中提炼出最具价值的核心观点&#xff0c;详细分析了数字化转型对企业的影响、实现路径&#xff0c;以及如何通过技术创新、文化转变和管理优化&#xff0c…

YOLO11涨点优化:注意力魔改 | 新颖的多尺度卷积注意力(MSCA),即插即用,助力小目标检测

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文全网首发独家改进&#xff1a;多尺度卷积注意力&#xff08;MSCA&#xff09;&#xff0c;有效地提取上下文信息&#xff0c;新颖度高&#xff0c;创新十足。 &#x1f4a1;&#x1f4a1;&#x1f4a1;本文改进&#xff1a;分别加入…