【C++笔记】十三、代码重用(下)

1.模板类

#include <iostream>
using namespace std;
typedef int Item;

class Stack
{
private:
    enum{MAX = 20};   // 栈的最大元素个数
    Item mItems[MAX]; // 存储栈的元素的数组
    int mTop;    // 指针,指向栈顶待写入数据的位置,而不是栈顶的元素的位置
public:
    Stack();
    bool isEmpty() const;     // 常量方法
    bool isFull() const;
    bool push(const Item& item);  // 压栈
    bool pop(Item& item);         // 出栈
    Item* getTop();            // 获得栈顶
};

Stack::Stack()
{
    mTop = 0;
}

bool Stack::isEmpty() const
{
    return mTop == 0;  // 注意这里是两个等号,用于判断,若二者相等返回1,不等返回0
}
bool Stack::isFull() const
{
    return mTop == MAX;
}

bool Stack::push(const Item& item)
{
    if(!isFull())
    {
        mItems[mTop++] = item; // 这里mTop使用后自加,因为mTop已经指向栈顶待写入元素的位置了,在该位置写入元素后,再自加指向下一个位置,就行了
        return true;
    }
    else
    {
        return false;
    }
}

bool Stack::pop(Item &item)
{
    if(!isEmpty())
    {
        item = mItems[--mTop];  // 这里用前减,先让指针指向栈顶元素位置,再出栈该元素,此时指针正好指向了栈顶元素的再往上一个位置,无需再操作
        return true;
    }
    else
    {
        return false;
    }
}

Item* Stack::getTop()
{
    if(!isEmpty())
    {
        return &mItems[mTop - 1];
    }
    else
    {
        return nullptr;
    }
}

int main(int argc, const char * argv[]) {
    // 模板类
    Stack stack;
    stack.push(20);
    stack.push(30);
    stack.push(40);
    Item item;
    stack.pop(item);   // 从pop的实现可以看出,出栈的时候,把出栈元素赋给了传入的参数item
    cout << "item = " << item << endl;   // 40
    cout << "stack top = " << *stack.getTop() << endl;  // 30
template <class StackType>  // 模板类,其中StackType是类型占位符
class TemplateStack
{
private:
    enum{MAX = 20};
    StackType mItems[MAX];   // StackType是类型占位符
    int mTop;
public:
    TemplateStack();
    bool isEmpty() const;
    bool isFull() const;
    bool push(const StackType& item);
    bool pop(StackType& item);
    StackType* getTop();
};

template <class StackType>  // 每次方法的实现前面必带这个句子
TemplateStack<StackType>::TemplateStack() // 写类名字的的地方要这样写:
// TemplateStack<StackType>才行,才是模板类的方法的实现
{
    mTop = 0;
}

template <class StackType>
bool TemplateStack<StackType>::isEmpty() const
{
    return mTop == 0;
}

template <class StackType>
bool TemplateStack<StackType>::isFull() const
{
    return mTop == MAX;
}

template <class StackType>
bool TemplateStack<StackType>::push(const StackType& item)
{
    if(!isFull())
    {
        mItems[mTop++] = item;
        return true;
    }
    else
    {
        return false;
    }
}

template <class StackType>
bool TemplateStack<StackType>::pop(StackType &item)
{
    if(!isEmpty())
    {
        item = mItems[--mTop];
        return true;
    }
    else
    {
        return false;
    }
}

template <class StackType>
StackType* TemplateStack<StackType>::getTop()
{
    if(!isEmpty())
    {
        return &mItems[mTop - 1];
    }
    else
    {
        return nullptr;
    }
}

int main(int argc, const char * argv[]) {
    
    //  使用TemplateStack
    TemplateStack<int> stack1;
    TemplateStack<string> stack2;
    stack1.push(20);
    stack2.push("hello world");
    cout << "stack2 top = " << *stack2.getTop() << endl;  // hello world

    return 0;
}

2.模板中的非类型参数

#include <iostream>
using namespace std; 

template <class Type, int n>  // class Type是类型参数,int n是非类型参数
class ArrayTP
{
private:
    Type mValues[n];
public:
    ArrayTP(Type values[]);
    ArrayTP(){}  // 最好是定义一个无参数的构造方法,当子类没有显式的调用父类的方法时,如果在此不定义无参数方法,那么就会报错,这里定义的就是空构造方法,以内联方式实现
    
    virtual Type& operator[](int i); // 使用虚函数重载操作符
    
};

template <class Type,int n>
ArrayTP<Type,n>::ArrayTP(Type values[])  // 模板类就得用 ArrayTP<Type, n>表示类名,不能只用ArrayTP
{
    for(int i = 0; i < n; i++)
    {
        mValues[i] = values[i];
    }
}

template <class Type,int n>
Type& ArrayTP<Type,n>::operator[](int i)
{
    if(i < 0 || i >= n)
    {
        cerr << "索引超出范围." << endl;
        exit(EXIT_FAILURE);
    }
    return mValues[i];  // 返回的是引用
}

int main(int argc, const char * argv[]) {

    // 模板中的非类型参数
    int values[] = {1,2,3,4,5};
    ArrayTP<int, 5> arrayTP;
    arrayTP = values;   // 这里会调用ArrayTP(Type values[]) 构造方法,把values[]都给到mValues[]

    int value = arrayTP[2];
    cout << "value = " << value << endl;  // 打印 :3
    cout << "arrayTP[100] = " << arrayTP[100] << endl; // 打印 :索引超出范围
    
    return 0;
}

3.模板的继承和组合

#include <iostream>
using namespace std;

template <class T>  // T是类型占位符
class TemplateClass
{
private:
    T mValue;
public:
    TemplateClass(T &value)
    {
        mValue = value;
    }
    T& getValue()
    {
        return mValue;
    }
};

template <class T>  // 定义子类时,也要加这句
class MyClass1 : public TemplateClass<T>  // 父类名字要这样写:TemplateClass<T>,不能只写名字
{
public:
    MyClass1(T &value) : TemplateClass<T>(value)  // 定义构造方法时,也要加 TemplateClass<T>(value)
    { 
        cout << "MyClass1" << endl;
    }
};

template <typename T> // 和template <class T> 作用一样,也可以把typename换成class
class MyClass2
{
public:
    TemplateClass<T> *templateClass;
    MyClass2(T &value)
    {
        templateClass = new TemplateClass<T>(value);
    }
    ~MyClass2()
    {
        delete templateClass;
    }
};

int main(int argc, const char * argv[]) {
    // 继承
    string s = "hello";
    MyClass1<string> myClass1(s);  // 指定MyClass1中的T为string,同时构造myClass1传入字符串s即"hello"
    cout << myClass1.getValue() << endl;  // hello
   // 组合
    long n = 1234;
    MyClass2<long> myClass2(n);
    cout << myClass2.templateClass->getValue() << endl;  // 1234

    return 0;
}

4.递归使用模板

#include <iostream>
using namespace std;

template <class T>
class TemplateClass
{
private:
    T mValue;
public:
    TemplateClass(T &value) : mValue(value) // 当T本身是模板类时,mValue就应该在这里显式的初
// 始化,而不是在构造方法里初始化,否则会报错
    {
       // mValue = value;
    }
    T& getValue()
    {
        return mValue;
    }
};

int main(int argc, const char * argv[]) {
    // 递归模板
    string s = "你好";
    TemplateClass<string> templateClass(s);
  // 也是模板类,只是把类型占位符T定义成了模板类型:TemplateClass<string>,
  // 那么所生成的模板类的传入参数,也应该是TemplateClass<string>类型
    TemplateClass<TemplateClass<string>> recursionTemplateClass(templateClass);
  // 这里也要使用两个getvalue()   
    cout << recursionTemplateClass.getValue().getValue() << endl; // 你好
    
    return 0;
}

5.拥有多个类型参数的模板

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

template <class T1, class T2, class T3>
class TemplateClass
{
private:
    T1 mValue1;
    T2 mValue2;
    T3 mValue3;
public:
    TemplateClass(T1 &value1, T2 &value2, T3 &value3) : mValue1(value1), mValue2(value2), mValue3(value3)  // 构造方法的定义,与参数的初始化
    {        
        
    }
};

int main(int argc, const char * argv[]) {

    // 多类型参数的模板    
    int n = 100;
    string s = "hello";
    vector<string> v;
    TemplateClass<int, string, vector<string>> templateClass(n, s, v); // 需要同时指定三个类型
    return 0;
}

6.默认类型参数

#include <iostream>
using namespace std;

template <class T1, class T2 = string> // 为T2指定默认类型为字符串
class TemplateClass
{
public:
    T1 value1;
    T2 value2;
};

int main(int argc, const char * argv[]) {
    // 默认类型参数

    TemplateClass<int> templateClass1; // 此时就可以只传一个类型参数了,第二个参数默认是string

    TemplateClass<int, char> templateClass2;

    return 0;
}

7.模板类的显式具体化

template <typename T>
class Compare
{
private:
    T mValue1;
    T mValue2;
public:
	// 通过构造方法将两个值传入类,同时初始化两个参数
    Compare(T &value1, T &value2) : mValue1(value1), mValue2(value2) 
    {
        
    }
    int getResult()
    {
        if(mValue1 > mValue2)
        {
            return 1;
        }
        else if(mValue1 < mValue2)
        {
            return -1;
        }
        else
        {
            return 0;
        }
    }
};

int main(int argc, const char * argv[]) {

    int n1 = 20, n2 = 30;
    Compare<int> compareInt(n1, n2);
    cout << compareInt.getResult() << endl;   // -1

    return 0;
}
template <typename T>
class Compare
{
private:
    T mValue1;
    T mValue2;
public:
	// 通过构造方法将两个值传入类,同时初始化两个参数
    Compare(T &value1, T &value2) : mValue1(value1), mValue2(value2) 
    {
        
    }
    int getResult()
    {
        if(mValue1 > mValue2)  // 这里是两个类的比较,会调用重载后的比较操作符:>
        {
            return 1;
        }
        else if(mValue1 < mValue2)
        {
            return -1;
        }
        else
        {
            return 0;
        }
    }
};

class MyClass
{
private:
    int mValue;
public:
    MyClass(int value)
    {
        mValue = value;
    }
    virtual bool operator<(MyClass& myClass)
    {
        return mValue < myClass.mValue;
    }
    virtual bool operator>(MyClass& myClass)
    {
        return mValue > myClass.mValue;
    }
};

int main(int argc, const char * argv[]) {
  
    MyClass myClass1(30), myClass2(20);
    Compare<MyClass> compareMyClass(myClass1, myClass2); // 这里是递归了模板类,把模板类作为
// T传给了Compare模板类,那么compareMyClass的两个传入参数就也应该是模板类
    cout << compareMyClass.getResult() << endl;

    const char *s1 = "h";  // 字符常量指针
    const char *s2 = "w";
    Compare<const char*> compareCharP(s1, s2); // T指定为字符常量指针,但这里比较的不是h和w
//的ASCII,而是s1和s2的地址,那么如果想比较s1和s2所指向的字符所对应的ASCII,就需要使用模板具体化
    cout << compareCharP.getResult() << endl;

    return 0;
}
template <typename T>
class Compare
{
private:
    T mValue1;
    T mValue2;
public:
	// 通过构造方法将两个值传入类,同时初始化两个参数
    Compare(T &value1, T &value2) : mValue1(value1), mValue2(value2) 
    {
        
    }
    int getResult()
    {
        if(mValue1 > mValue2)
        {
            return 1;
        }
        else if(mValue1 < mValue2)
        {
            return -1;
        }
        else
        {
            return 0;
        }
    }
};

// 模板具体化:阻止编译器为const char*类型生成相应的类,而是使用自己定义的类
// 即,当使用模板生成类时,若T被指定为const char*,则调用这个自定义的模板,不再调用上面的通用模板
template<> class Compare<const char*>
{
private:
    const char* mValue1; // 指针
    const char* mValue2;
public:
// 定义构造方法,同时传入常量字符指针,同时初始化参数
    Compare(const char* value1, const char* value2):mValue1(value1), mValue2(value2)
    {
        
    }
    int getResult()
    {
        int result = strcmp(mValue1, mValue2); // 使用了strcmp函数,传入的是指向字符串首地址的指针
        if(result > 0)
        {
            return 1;
        }
        else if(result < 0)
        {
            return -1;
        }
        else
        {
            return 0;
        }
    }
};

int main(int argc, const char * argv[]) {

    // 模板类的显式具体化
    const char *s1 = "w";
    const char *s2 = "h";
    Compare<const char*> compareCharP(s1, s2);
    cout << compareCharP.getResult() << endl;  // 返回 1
    
    return 0;
}

8.模板类的部分具体化

1.  类型部分具体化

#include <iostream>
using namespace std;
template <class T1, class T2>
class TemplateClass
{
private:
    T1 mValue1;
    T2 mValue2;
public:
    TemplateClass(T1 &value1, T2 &value2)
    {
        mValue1 = value1;
        mValue2 = value2;
        cout << "通用的模板:TemplateClass(T1 &value1, T2 &value2)" << endl;
    }
};

//  完全具体化
template <> class TemplateClass<int, string> // T1具体化为int类型,T2具体化为string类型
{
private:
    int mValue1;
    string mValue2;
public:
    TemplateClass(int &value1, string &value2)
    {
        mValue1 = value1;
        mValue2 = value2;
        cout << "完全具体化:TemplateClass(int &value1, string &value2)" << endl;
    }
};

//  部分具体化
template <class T1> class TemplateClass<T1, string> // 保留T1,T2具体化为string
{
private:
    T1 mValue1;
    string mValue2;
public:
    TemplateClass(T1 &value1, string &value2)
    {
        mValue1 = value1;
        mValue2 = value2;
        cout << "部分具体化:TemplateClass(T1 &value1, string &value2)" << endl;
    }
};

// 使用模板类时,编译器先考虑完全具体化的类,再考虑部分具体化,若前二者都不满足条件才会考虑通用模板
int main(int argc, const char * argv[]) {

    // 模板类的部分具体化
    //  1.  类型部分具体化
    //  2.  指针部分具体化
    
    int valueInt = 20;
    string valueString = "Bill";
    
    TemplateClass<int, string> templateClass1(valueInt, valueString); // 完全具体化
    TemplateClass<string, string> templateClass2(valueString, valueString); // 部分具体化
    TemplateClass<string, int> templateClass3(valueString, valueInt); // 通用模板
    
    
    PointerTemplateClass<int> pointerTemplateClass1;
    PointerTemplateClass<int*> pointerTemplateClass2;
    
    return 0;
}

2.  指针部分具体化

把T的类型限定为指针类型

template <class T>
class PointerTemplateClass
{
public:
    PointerTemplateClass()
    {
        cout << "通用模板" << endl;
    }
};

template<class T> class PointerTemplateClass<T*>
{
public:
    PointerTemplateClass()
    {
        cout << "指针部分具体化" << endl;
    }
};

int main(int argc, const char * argv[]) {
     
    PointerTemplateClass<int> pointerTemplateClass1; // 通用模板
    PointerTemplateClass<int*> pointerTemplateClass2;  // 指针部分具体化
    
    return 0;
}

9.成员模板

即模板类中的成员还是模板类

#include <iostream>
using namespace std;

template <typename T>
class MyClass {
private:
	
    template <typename V> // 定义模板类前面必须跟这句
    class TemplateClass1
    {
    public:
        V value;
    };
    
    template <typename V1, typename V2>
    class TemplateClass2
    {
    public:
        V1 value1;
        V2 value2;
    };
    
    template<typename V1> class TemplateClass2<V1, string>//TemplateClass2的部分具体化
    {
    public:
        V1 value1;
        string value2;
        TemplateClass2()
        {
            cout << "部分具体化" << endl;
        }
    };
    
    template <typename V>
    class TemplateClass3; // 只声明,未实现
    
public:
    TemplateClass1<T> templateClass1; // 先不给T指定类型
    TemplateClass2<T, int> templateClass2_1; // 不符合部分具体化,使用通用模板类
    TemplateClass2<int, string> templateClass2_2; // 部分具体化
};

// 这里需要写两个这种句子,因为TemplateClass3是作为模板成员在一个模板类中的
// 上面写MyClass的句子,下面写TemplateClass3的句子
template <typename T>
    template <typename V>  
class MyClass<T>::TemplateClass3  // 记住这种写法
{
public:
    V value;
};

int main(int argc, const char * argv[]) {
    // 成员模板
    MyClass<string> myClass; // Myclass的传入参数是T,即指定T为string
    
    return 0;
}

10.将模板类作为类型参数的类型

#include <iostream>
using namespace std;

template <class TT1, class TT2>
class Person
{
    
};

template <class TT1>
class Person1
{
    
};

class TestClass
{
    
};

// 这个模板类的类型参数还是模板类
// 其中P是类型参数的名称,template<class T1, class T2> class是类型参数的类型
// 即,P的类型必须是模板,且这个模板必须有两个参数
template<template<class T1, class T2> class P> 
class MyClass
{
    
};

int main(int argc, const char * argv[]) {
    // 将模板类作为类型参数的类型
    MyClass<Person> myClass1;
   // MyClass<TestClass> myClass2; // 报错,因为TestClass的参数个数不是2
   // MyClass<Person1> myClass3; // 报错,因为Person1的参数个数不是2
    
    return 0;
}

11.模板类与友元函数

    友元函数:把一个函数定义成和一个类有关的友元函数,那么在这个函数中就可以访问类的私有成员和受保护的成员

#include <iostream>
using namespace std;

template <typename T>
class TemplateClass
{
private:
    T mValue;
public:
    TemplateClass(T value)
    {
        mValue = value;
    }
    friend void printSize();    // 友元函数
};

template<typename T>   // 模板类中的函数,在函数实现时也要加上这句
void printSize()
{
    cout << "TemplateClass size:" << sizeof(TemplateClass<T>) << endl;  //TemplateClass需要加上类型参数
}

int main(int argc, const char * argv[]) {

    //  模板类与友元
    //  1.  非模板友元
    //  2.  使用类模板类型参数的友元
    //  3.  使用自己的类型参数的友元
    printSize<int>();        //  4 (字节)
    printSize<long long>();  //  8  (字节)

    return 0;
}
#include <iostream>
using namespace std;

//  第3步  在模板类的前面声明友元函数
template<typename T> void print(T& t);  // 声明

template <typename T>
class TemplateClass
{
private:
    T mValue;
public:
    TemplateClass(T value)
    {
        mValue = value;
    }

//其中空的尖括号表示print是使用类型参数的,后面的括号里面是在指定参数类型:TemplateClass<T>对象的引用类型
    friend void print<>(TemplateClass<T>&);  //  第1步 在模板类中声明友元函数

};

//  第2步 在模板类的外部实现友元函数
template<typename T>
void print(T& t)
{
    cout << t.mValue << endl;
}

class MyClass
{
private:
    int mCode =200;
public:
    template<typename T1, typename T2> friend void show(T1&, T2&); // 这整体是一个在类中声明的函数模板,前面是声明了类型参数T1和T2,后面是友元函数
};

template <typename T1, typename T2> void show(T1& t1, T2& t2)
{
    cout << t1.mCode << endl;
    cout << t2 << endl;
}

int main(int argc, const char * argv[]) {

    //  实现友元函数,需要完成如下3步:
    //  (1)  在模板类中声明友元函数
    //  (2)  在模板类的外部实现友元函数
    //  (3)  在模板类的前面声明友元函数
    
    TemplateClass<int> templateClass(123); 
    print(templateClass);   //   123  (这个print是简写,省略了尖括号中的参数类型)
    print<TemplateClass<int>>(templateClass);  // 这个是完全版
    
    MyClass myClass;
    string s = "hello world";
    show(myClass, s);   //  200  hello world
    
    return 0;
}

12.模板别名(C++ 11)

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

typedef array<int, 20> arri;    // 用typedef为一个比较长的类型定义别名
typedef array<double, 10> arrd; // 缺点在于,当array中不想用double,想用long时,就还得用typedef定义一次,不灵活

//  using
template <typename T> // 是要使用类型参数(类型占位符)T,就要写上template,否则编译器不知道是什么东西
using arr = array<T, 20>;  // 这样就可以通过给arr传参,从而控制array的类型了

typedef const char* pc1; 
using pc2 = const char*; // 和上式效果相同

int main(int argc, const char * argv[]) {
    // 模板别名
    //  using 是C++ 11新提供的特性,可用于模板和非模板
    pc1 p1;
    pc2 p2;

    arri a1;
    arrd a2;
    
    array<double, 20> a;
    arr<double> a3;
    arr<char> a4;
    a3[0] = 12.2; 
    cout << a3[0] << endl;

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

DUANDAUNNN

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

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

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

打赏作者

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

抵扣说明:

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

余额充值