c++模板与泛型编程

模板的作用:通过使用模板降低重复代码的编写,把已经写好的代码作用最大化;

模板的概念

1.模板与泛型编程(目的时只进行逻辑操作,不需要考虑数据类型,将类型作为参数传递)

模板是实现代码重用的一种工具或方法,用于完成某种功能或解决某种问题,模板的作用就是实现类型参数化。

模板就是泛型编程的前置条件,属于充分但不必要条件;

总而言之:泛型编程就是为了编写和数据类型没有关系的逻辑代码,而模板就是为了实现代码逻辑和数据类型没有关系抽象的数据类,是泛型编程的先决条件;

2.模板分类

函数模板:

函数模板用于定义模板函数,模板函数就是用模板生成的函数;

类模板

类模板用于定义模板类,模板类就是用类模板定义出来的类;

总而言之:模板作用于函数就是函数模板,模板作用于类就是类模板;

函数模板和普通函数对比:

1.只要满足重载条件都可以构成重载,模板函数和普通函数之间只要满足重载条件都可以构成重载;

2.同时出现模板函数和普通函数重载,相同情况下编译器默认优先调用普通函数;

3.如果需要强制调用模板函数,可以使用显式调用的方式去调用指定的模板函数;

4.模板的局限性

// 通过函数模板定义模板函数
// 语法:
// 定义:
// template用于定义模板的关键字,typename(class)用于定义类型的关键字,Type_n为自定义的类型名称
// template <typename Tpe_1, ... , typename Type_n>
// 返回值类型 函数名(形参列表)
// {
//      函数体;
// }
// 调用:
// 函数名<指定类型>(参数列表)

#include <iostream>
#include <string>

using namespace std;

template <typename T1, typename T2>
T1 add(T1 a, T2 b)
{
    return a + b;
}

// class也可以用于定义类型,但是一般都用typename代替class就行
template <class TT1, class TT2>
void test1(TT1 tt1, TT2 tt2)
{
    cout << "tt1 = " << tt1 << ", tt2 = " << tt2 << endl;
}

// class也可以用于定义类型,但是一般都用typename代替class就行
template <typename TY1, typename TY2>
void test2(TY1 tt1, TY2 tt2)
{
    cout << "tt1 = " << tt1 << ", tt2 = " << tt2 << endl;
}

int main()
{
    // test1
    // 隐式推导类型,由编译器指定
    cout << "test1 : " << endl;
    test1(1, 2);
    test1(1.123, 2.123);
    test1("name", 'A');
    // test2
    cout << "test2 : " << endl;
    test2(1, 2);
    test2(1.123, 2.123);
    test2("name", 'A');

    // add测试
    cout << "add(3, 4) = " << add(3, 4) << endl;
    cout << "add(1.23, 1.24) = " << add(1.23, 1.24) << endl;
    // add(6, 2.46) = 8 输出的数据类型由编译器决定,这里返回值设置为int类型
    cout << "add(6, 2.46) = " << add(6, 2.46) << endl;
    cout << "add<int>(6, 2.46) = " << add<int>(6, 2.46) << endl;
    cout << "add<double>(6, 2.46) = " << add<double>(6, 2.46) << endl;
    // 模板函数调用,显式指定类型,告诉编译器按照指定类型操作
    // const char *就是string类型
    test1<const char *, char>("name", 'A');

    return 0;
}

// 模板的局限性
#include <iostream>
#include <string>

using namespace std;

template <typename T>
bool IsDataEqual(T a, T b)
{
    if (a == b) {
        return true;
    }
    return false;
}

class MyData
{
public:
    int m_num;
    int m_val;
    // 添加默认构造函数,如果有自定义构造函数后系统将不会再分配默认的构造函数
    // 如果没有没有这个无参构造函数,那么通过MyData obj1, obj2;方式定义对象时会报错如下:
    // error: no matching function for call to ‘MyData::MyData()’MyData obj1, obj2;
    MyData() : m_num(0) {}
    // 自定义带参构造函数
    MyData(int n) : m_num(n) {}
};

class MyClass
{
public:
    int m_num;
    int m_val;
    // 如果加上设置默认缺省参数值,可以使用MyClass obj创建对象,此时这个对象拥有设置的默认缺省参数值
    MyClass(double n = 5.0, double val = 6.0) : m_num(n), m_val(val) {}
};


// 对自定义数据类型的对象需要自定义规则进行比较
template<typename TT> bool MyCompare(const TT &a, const TT &b)
{
    if (a.m_num == b.m_num) {
        return true;
    }
    return false;
} 

int main()
{
    int num = 5, val = 5;
    cout << boolalpha << IsDataEqual(num, val) << endl;
    val = 6;
    cout << (IsDataEqual(num, val) ? "true" : "false") << endl;
    cout << (IsDataEqual<int>(num, val) ? "true" : "false") << endl;

    // 自定义数据类型比较
    MyData obj1(5), obj2(5);
    // 如果没有重载==运算符,不同对象之间不可以使用模板函数进行比较
    cout << (MyCompare(obj1, obj2) ? "true" : "false") << endl;
    cout << (MyCompare<MyData>(obj1, obj2) ? "true" : "false") << endl;

    MyClass obj;
    cout << "obj.m_num = " << obj.m_num << ", obj.m_val = " << obj.m_val << endl;

    return 0;
}

// 模板类
// 语法:
// template<类型参数列表>
// class 模板类类名
// {
//     成员;
// }; // 定义域需要加";"

// 类型参数列表:<class T1, ..., class Tn>
// 成员:T1 name; ... Tn

#include <iostream>
#include <string>

using namespace std;

// 数据参数可用默认值,数据类型也可以使用默认值
// 默认类型可以有,也可以不写
// template <class Type1 = int, class Type2 = double>
template <class Type1, class Type2>
class MyData
{
private:
    Type1 m_num;
    Type2 m_val;
public:
    MyData(Type1 n = 0, Type2 v = 0.0) : m_num(n), m_val(v) {}
    // get/set 内联
    Type1 GetNum() {return m_num;}
    Type2 GetVal() {return m_val;}
    void SetNum(Type1 n) {m_num = n;}
    void SetVal(Type2 v) {m_val = v;}

    void showData();
};

// 模板类需要给定类型才能完整,没给类型就不是完整类
template <typename T1, typename T2>
void MyData<T1, T2>::showData()
{
    cout << "num = " << m_num << ", val = " << m_val << endl;
}

// 1.普通全局函数
// 普通全局函数,指定MyData类型的对象并指定模板类对象的参数
// 这种写法只能接收类型匹配的参数对象
void testFunc_1(MyData<int, double> &obj)
{
    cout << "testFunc_1(MyData<int, double> &obj) : " ;
    obj.showData();
}

// 2.函数参数模板化
template <typename TT1, typename TT2>
void testFunc_2(MyData<TT1, TT2> & obj)
{
    cout << "testFunc_2(MyData<TT1, TT2> & obj)  : " ;
    obj.showData();
}

// 3.整个类作为一个类型,整体参数作为一个模板传输
template <class T>
void testFunc_3(T & obj)
{
    cout << "testFunc_3(T & obj)  : " ;
    obj.showData();
}

int main()
{
    // 如果没有设置默认参数类型则需要指定参数类型
    // template <class Type1, class Type2> // 未指定默认参数类型
    MyData<int, double> data_1(3, 3.14);
    testFunc_1(data_1);
    // 如果有设置默认参数类型,<>中可以不指定数据类型
    // template <class Type1 = int, class Type2 = double> // 指定默认参数类型
    // MyData<> data_1(3, 3.14);
    data_1.showData();

    // 模板类可以在创建对象的时候指定对象中的类型
    MyData<char, double> data_2(67, 3.14);
    data_2.showData();
    // 隐式调用,不指定参数类型
    testFunc_2(data_2);
    // 显示调用,指定参数类型
    testFunc_2<int, double>(data_1);

    MyData<char, float> data_3(68, 3.14);
    data_3.showData();
    // 通过以上测试可以看出同一种类型的对象数据类型不一样,这就是模板类的功能

    testFunc_3(data_1);
    testFunc_3<MyData<int, double>>(data_1);
    testFunc_3(data_2);
    testFunc_3<MyData<char, double>>(data_2);
    testFunc_3(data_3);
    testFunc_3<MyData<char, float>>(data_3);

    return 0;
}

// 类模板的继承
// 注意事项:
// 写模板时需要将类的声明和实现都写在同一个文件中
#include <iostream>
#include <string>

using namespace std;

template <class FType>
class Father
{
public:
    FType m_f_val;
};

// 子类继承父类时需要指定参数类型
class Son : public Father<int>
{
public:
    int m_s_val;
};

// 子类本省也可以是一个模板
template <class SType1, class SType2>
class Son1 : public Father<SType1>
{
public:
    int m_s_val;
};

int main()
{
    Son1<int, double> obj_son;
    obj_son.m_f_val;
    obj_son.m_s_val;

    return 0;
}

// 模板与友元之间的关系
// 相当于就是在类模板中使用友元函数
#include <iostream>
#include <string>

using namespace std;

// 模板类友元函数能不用就不用,
// 如果非得要用,在类中声明时需要在函数名后边加上一个<>,实现函数体定义的时候不能在函数名后边加<>
// 需要在模板类友元函数前面先声明类
// 还需要在模板类友元函数前面声明函数
// 以下4行声明代码其实只需要先声明void show_2(A<T> & obj);函数即可
// 但是void show_2(A<T> & obj);函数声明的时候需要声明类型T,即template <typename T>
// template <typename T>声明类型T的时候发现类A也需要提前声明
// class A;声明类A的时候也得提前声明模板类型,即template <class T>
// 所以就有了以下4行声明
template <class T>
class A;
template <typename T>
void show_2(A<T> & obj);

// 只设置一个类型,并设置默认参数类型为int,当然也可以不设置默认类型
template <class T = int>
class A
{
public:
    A(T t = 0) : m_a(t) {}

private:
    T m_a;

    // C++ 中友元函数 `friend` 的权限与其在类中声明的访问权限是无关的,
    // 即无论它是在 `public`、`protected` 或 `private` 声明的,它的访问权限都是类外的,可以访问类的私有成员。
    // 当在类中声明一个函数为 `friend`,其实质上是将这个函数的访问权限放宽到了类的外部,使它可以访问类的私有成员。
    // 因此,友元函数的访问权限不受类的访问控制修饰符所限制。
    // 需要注意的是,友元关系是单向的,即如果类 A 作为类 B 的友元,不一定意味着类 B 作为类 A 的友元。
    // 另外,友元关系是不能被继承的,即派生类无法访问基类中的友元函数。
    friend void show_1(A<T> & obj) {
        cout << "show_1() : " << obj.m_a << endl;
    }
    // 其实友元函数一般只在类中声明,类外定义
    // show_2中的T与上文中的T没有任何联系,巧合,都称之为T
    // 只有在声明的时候需要在函数名后边写<>,在实现定义的时候不需要加<>
    friend void show_2<>(A<T>& obj);

};

template <typename T>
// 只有在声明的时候需要在函数名后边写<>,在实现定义的时候不需要加<>
void show_2(A<T> & obj)
{
    cout << "show_2() : " << obj.m_a << endl;
}

int main()
{
    A<int> obj_a(666);
    show_1(obj_a);
    show_2(obj_a);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值