10C++模板泛型编程

模板泛型编程

1.简介

​ 泛型,是在运行时确定类型,而不是在编译时确定类型。主要是将类型通用化,减少代码量,实现通用化。

2.函数模板

等价于java中泛型函数,

template<typename T> 通用模板,T主要是告诉编译器后面代码中T不要报错,在运行时确定类型。

#include <iostream>
using namespace std;
template <typename T>
void swap2(T &a, T &b)
{
    T temp = a;
    a = b;
    b = temp;
}

int main(int argc, char const *argv[])
{
    int a = 10;
    int b = 20;
    swap2(a, b);
    cout << a << b << endl;
    system("pause");
    return 0;
}

mySwap<int>(a, b);//可以直接确定类型

3.泛型模板注意事项

1.推导的类型,必须一致

2.模板必须要确定出T的具体类型,才可以使用

4.函数模板2

template 能用于函数模板中

template <class T>
void swap2(T &a, T &b)
{
    T temp = a;
    a = b;
    b = temp;
}
#include <iostream>
using namespace std;
template <typename T>
class Complex
{
public:
    //构造函数
    Complex(T a, T b)
    {
        this->a = a;
        this->b = b;
    }

    //运算符重载
    Complex<T> operator+(Complex &c)
    {
        Complex<T> tmp(this->a + c.a, this->b + c.b);
        return tmp;
    }

private:
    T a;
    T b;
};

int main()
{
    //对象的定义,必须声明模板类型,因为要分配内容
    Complex<int> a(10, 20);
    Complex<int> b(20, 30);
    Complex<int> c = a + b;
    system("pause");
    return 0;
}

5.普通函数和泛型函数区别

1.普通函数调用可以发生隐式转换

2.函数模板 用自动类型推导,不可以发生隐式类型转换

3.函数模板用显示指定类型,可以发生隐式类型转换.

隐式转换:

#include <iostream>
#include <string>
using namespace std;
void swap3(int a, int c)
{
    int temp = c;
    c = a;
    a = temp;
}
int main(int argc, char const *argv[])
{
    int a = 20;
    char c = 'c';
    swap3(a, c);
    system("pause");
    return 0;
}

2.函数模板不可以发生隐式类型转换

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

template <typename T>
void swap3(T a, T c)
{
    T temp = c;
    c = a;
    a = temp;
}

int main(int argc, char const *argv[])
{
    int a = 20;
    char c = 'c';
    swap3(a, c);//报错
    system("pause");
    return 0;
}

3.指定类型,发生隐式转换

    swap3<int>(a, c);//不报错

6.普通函数与泛型函数调用规则

1.如果函数模板和普通函数都可以实现,普通优化

2.可以使用空模板参数列表来强制调用函数模板

3.函数模板也可以发生重载

4.如果函数模板剋产生更好的匹配,有限调用函数模板

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

void swap4(int &a, int &b)
{
    cout << "普通函数调用了" << endl;
    int temp = a;
    a = b;
    b = temp;
}

template <typename T>
void swap4(T &a, T &b)
{
    cout << "函数模板调用了" << endl;
    T temp = a;
    a = b;
    b = temp;
}

int main(int argc, char const *argv[])
{
    int a = 10;
    int b = 20;
    swap4(a, b);//普通函数调用了
    swap4<>(a, b);//函数模板调用了,加空模板
    system("pause");
    return 0;
}

3模板重载

#include <iostream>
#include <string>
using namespace std;
void swap4(int &a, int &b)
{
    cout << "普通函数调用了" << endl;
    int temp = a;
    a = b;
    b = temp;
}

template <typename T>
void swap4(T &a, T &b)
{
    cout << "函数模板调用了" << endl;
    T temp = a;
    a = b;
    b = temp;
}

template <typename T>
void swap5(T &a, T &b, T &c)
{
    cout << a << b << c;
}

int main(int argc, char const *argv[])
{
    int a = 10;
    int b = 20;
    int c = 30;
    swap4(a, b);
    swap4<>(a, b);
    swap5(a, b, c);
    system("pause");
    return 0;
}

4.如果函数模板剋产生更好的匹配

7.模板的通用性

模板有其限制性,比如比较时,比较基本类型,但是如果是比较类对象,则可能会有问题。

在争对非基本类型时,有其局限性,比如比较。

#include <iostream>
using namespace std;
template <typename T1, typename T2>
class Base
{
public:
    T1 name;
    T2 age;

public:
    Base(T1 name, T2 age);
};

template <typename T1, typename T2>
Base<T1, T2>::Base(T1 name, T2 age)
{
    this->name = name;
    this->age = age;
}

//比较
template <typename T1, typename T2>
bool compare(Base<T1, T2> &b1, Base<T1, T2> &b2)
{
    if (b1.name == b2.name && b1.age == b2.age)
    {
        return true;
    }
    return false;
}

void test()
{
    Base<string, int> base("zhangsan", 20);
    Base<string, int> base2("zhangsan", 20);
    bool flag = compare(base, base2);
    cout << flag << endl;
}

int main(int argc, char const *argv[])
{
    test();
    system("pause");
    return 0;
}

8.模板显示化调用

#include <iostream>
using namespace std;
template <typename T>
void swap5(T &a, T &b);

class Job
{
public:
    string name;
    int age;

public:
    Job(string name, int age);
};

Job::Job(string name, int age)
{
    this->name = name;
    this->age = age;
}
template <>
void swap5(Job &job1, Job &job2)
{
    string tempName = job1.name;
    int tempAge = job1.age;
    job1.name = job2.name;
    job1.age = job2.age;
    job2.name = tempName;
    job2.age = tempAge;
}

template <typename T>
void swap5(T &a, T &b) // general version
{
    T temp;
    temp = a;
    a = b;
    b = temp;
}

void test()
{
    int a = 10;
    int b = 20;
    swap5(a, b);

    Job job1("zhangsan", 20);
    Job job2("lisi", 30);
    swap5<>(job1, job2);//具显化调用
}
int main(int argc, char const *argv[])
{
    test();
    system("pause");
    return 0;
}

9.模板实例化调用

#include <iostream>
using namespace std;
template <typename T1, typename T2>
class Base
{
public:
    T1 name;
    T2 age;

public:
    Base(T1 name, T2 age);
    T1 getName() const;
    T2 getAge() const;
};

template <typename T1, typename T2>
Base<T1, T2>::Base(T1 name, T2 age)
{
    this->name = name;
    this->age = age;
}

template <typename T1, typename T2>
T1 Base<T1, T2>::getName() const
{
    return this->name;
}

template <typename T1, typename T2>
T2 Base<T1, T2>::getAge() const
{
    return this->age;
}

void test()
{
    Base<string, int> base("zhangsan", 20);
    cout << base.getName() << base.getAge() << endl;
}

int main(int argc, char const *argv[])
{
    test();
    system("pause");
    return 0;
}

10.模板多文件编程

1.当不涉及泛型类模板时

类的声明在.h文件中,类的实现在.cpp文件中,主程序main.cpp文件,只需添加.h文件就可以调用,或者调用.cpp也可以

原因:调用.h文件时,不是泛型模板,则编译时,就会去找实现,全局声明。

main.cpp

#include <stdio.h>
#include <stdlib.h>
#include "01fun.h"

int main(int argc, char const *argv[])
{
    char *s1 = (char *)"hello";
    char *s2 = (char *)"world";
    char *s3 = strCatStr(s1, s2);
    printf("%s\n", s3);
    free(s3);
    system("pause");
    return 0;
}

01fun.h

#ifndef FUN_01
#define FUN_01
char *strCatStr(char *arr1, char *arr2);
#endif

01fun.c

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <01fun.h>

char *strCatStr(char *arr1, char *arr2)
{
    char *result = (char *)malloc(strlen(arr1) + strlen(arr2) + 1); //1作为结束位置'\0',不会默认添加'\0'
    if (result == NULL)
    {
        exit(1);
    }
    char *temp = result; //记录首地址
    while (*arr1 != '\0')
    {
        *result = *arr1;
        arr1++;
        result++;
    }
    while (*arr2 != '\0')
    {
        *result = *arr2;
        arr2++;
        result++;
    }
    *result = '\0';//作为结束标志
    printf("%c\n", *result);
    return temp;
}
2.当涉及泛型类模板时

两种形式:

1.类声明.h,类实现.cpp,主程序main.cpp,但是只能添加.cpp才能执行类模板

2.类声明实现.hpp,主程序main.cpp,添加.hpp文件执行类模板。

原因:泛型类模板和函数模板,编译器不会再编译器时就确定要执行哪个文件,所以声明的.h文件不会去实现.cpp中的函数模板,所以要不直接执行.cpp文件,要不声明.h和.cpp结合,否则会编译失败

main.cpp

#include <iostream>
#include "11func.cpp"
using namespace std;
void test()
{
    BaseAdapter<string, int> BaseAdapter("zhangsan", 20);
    cout << BaseAdapter.getName() << BaseAdapter.getAge() << endl;
}

int main(int argc, char const *argv[])
{
    test();
    system("pause");
    return 0;
}

11func.h

template <typename T1, typename T2>
class BaseAdapter
{
public:
    T1 name;
    T2 age;

public:
    BaseAdapter(T1 name, T2 age);
    T1 getName() const;
    T2 getAge() const;
};

11func.cpp

#include "11func.h"

template <typename T1, typename T2>
BaseAdapter<T1, T2>::BaseAdapter(T1 name, T2 age)
{
    this->name = name;
    this->age = age;
}

template <typename T1, typename T2>
T1 BaseAdapter<T1, T2>::getName() const
{
    return this->name;
}

template <typename T1, typename T2>
T2 BaseAdapter<T1, T2>::getAge() const
{
    return this->age;
}

如果使用.hpp文件

template <typename T1, typename T2>
class BaseAdapter
{
public:
    T1 name;
    T2 age;

public:
    BaseAdapter(T1 name, T2 age);
    T1 getName() const;
    T2 getAge() const;
};

template <typename T1, typename T2>
BaseAdapter<T1, T2>::BaseAdapter(T1 name, T2 age)
{
    this->name = name;
    this->age = age;
}

template <typename T1, typename T2>
T1 BaseAdapter<T1, T2>::getName() const
{
    return this->name;
}

template <typename T1, typename T2>
T2 BaseAdapter<T1, T2>::getAge() const
{
    return this->age;
}

11.hpp模板使用注意点

  • 1.不可包含全局对象和全局函数。
  • 2.类之间不可循环调用
  • 3.不可使用静态成员

3唯一的列外,唯一的例外是const static整型成员,因为在vs2003中,该类型允许在定义时初始化。

class A{
public:
	const static int intValue = 123;
};

1.类中仅有一个静态成员时,且仅有一个调用者时,可以通过局域静态变量模拟

//方法模拟获取静态成员
someType getMember()
{
	static someTypevalue(xxx);//作用域内静态变量
	return value;
}

2.类中有多个方法需要调用静态成员,而且可能存在多个静态成员时,可以将每个静态成员封装一个模拟方法,供其他方法调用。

someType getMemberA()
{
	static someTypevalue(xxx);//作用域内静态变量
	return value;
}

someType getMemberB()
{
	static someTypevalue(xxx);//作用域内静态变量
	return value;
}

void accessMemberA()
{
	someType member = getMemberA();//获取静态成员
};

//获取两个静态成员

void accessStaticMember()
{
       someType a  = getMemberA();//获取静态成员
       someType b = getMemberB();
};

3.第二种方法对于大部分情况是通用的,但是当所需的静态成员过多时,编写封装方法的工作量将非常巨大,在此种情况下,建议使用Singleton模式,将被调用类定义成普通类,然后使用Singleton将其变为全局唯一的对象进行调用。

如原h+cpp下的定义如下:

class A{
public :
	type getMember(){
		return member;
	}

	static type member;//静态成员
}
12345678

采用singleton方式,实现代码可能如下(singleton实现请自行查阅相关文档)

//实际实现类
class Aprovider{
public :
        type getMember(){
           return member;
        }
        
        type member;//变为普通成员
}

//提供给调用者的接口类
class A{
public :
        type getMember(){
           return Singleton<AProvider >::getInstance()->getMember();
        }
}   

12.泛型类模板继承

1.子类继承父类

与其他语言差不多,java中也是一样,需要实现泛型函数类型,编译器需要确定

#include <iostream>
using namespace std;

template <typename T1, typename T2>
class Base
{
public:
    T1 name;
    T2 age;

public:
    Base(){};
    Base(T1 name, T2 age);
    T1 getName() const; //这只是虚函数,并不是纯虚函数,Base不是抽象类,用于解决多态问题
    T2 getAge() const;
    virtual void show() const;
};

template <typename T1, typename T2>
void Base<T1, T2>::show() const
{
    cout << this->name << this->age << endl;
}

template <typename T1, typename T2>
Base<T1, T2>::Base(T1 name, T2 age)
{
    this->name = name;
    this->age = age;
}

template <typename T1, typename T2>
T1 Base<T1, T2>::getName() const
{
    return this->name;
}

template <typename T1, typename T2>
T2 Base<T1, T2>::getAge() const
{
    return this->age;
}

class Son : public Base<string, int>
{
public:
    int salary;

public:
    Son(string name, int age, int salary);
    string getName() const;
    virtual void show() const;
};

Son::Son(string name, int age, int salary) : Base(name, age)
{
    this->salary = salary;
}

string Son::getName() const
{
    return this->name;
}

void Son::show() const
{
    cout << this->name << this->age << "薪资:"<<this->salary << endl;
}

void test()
{
    Base<string,int> *son = new Son("zhangsan", 20, 10000);
    son->show();
}

int main(int argc, char const *argv[])
{
    test();
    system("pause");
    return 0;
}
2.子类也是泛型模板

子类是泛型模板,则可以等到实现的时候去确定类型,泛型,编译器会过滤,等到执行的时候才会运行。

#include <iostream>
using namespace std;

template <typename T1, typename T2>
class Base
{
public:
    T1 name;
    T2 age;

public:
    Base(){};
    Base(T1 name, T2 age);
    T1 getName() const; //这只是虚函数,并不是纯虚函数,Base不是抽象类,用于解决多态问题
    T2 getAge() const;
    virtual void show() const;
};

template <typename T1, typename T2>
void Base<T1, T2>::show() const
{
    cout << this->name << this->age << endl;
}

template <typename T1, typename T2>
Base<T1, T2>::Base(T1 name, T2 age)
{
    this->name = name;
    this->age = age;
}

template <typename T1, typename T2>
T1 Base<T1, T2>::getName() const
{
    return this->name;
}

template <typename T1, typename T2>
T2 Base<T1, T2>::getAge() const
{
    return this->age;
}

class Son : public Base<string, int>
{
public:
    int salary;

public:
    Son(string name, int age, int salary);
    string getName() const;
    virtual void show() const;
};

Son::Son(string name, int age, int salary) : Base(name, age)
{
    this->salary = salary;
}

string Son::getName() const
{
    return this->name;
}

void Son::show() const
{
    cout << this->name << this->age << "薪资:"<<this->salary << endl;
}

void test()
{
    Base<string,int> *son = new Son("zhangsan", 20, 10000);
    son->show();
}

int main(int argc, char const *argv[])
{
    test();
    system("pause");
    return 0;
}
3.继承 A->B->C
#include <iostream>
using namespace std;

template <typename T>
class IBase //当作接口使用,全部都是纯虚抽象函数,则基本等价于java中的接口概念
{
public:
    virtual void setSalary(T salary) = 0;
    virtual T getSalary() const = 0;
};

template <typename T>
class Base : public IBase<T>
{
public:
    int age;
    T baseSalary;

public:
    virtual void setAge(int age) = 0;
};

class Student : public Base<string>
{
public:
    virtual void setSalary(string salary);
    virtual string getSalary() const;
    virtual void setAge(int age);
};

void Student::setSalary(string salary)
{
    this->baseSalary = salary;
}

string Student::getSalary() const
{
    return this->baseSalary;
}

void Student::setAge(int age)
{
    this->age = age;
}

void test()
{
    Student stu;
    stu.setAge(20);
    stu.setSalary("10000");
    cout << stu.getSalary() << "年龄:" <<stu.age << endl;
}

int main(int argc, char const *argv[])
{
    test();
    system("pause");
    return 0;
}

13.类模板与友元

1.友元函数声明非模板函数

非模板函数,如果只是使用一个模板作为参数,这意味着必须要为使用的友元定义显式具体化

#include <iostream>
using namespace std;

template <class T>
class Base
{
private:
    string name;
    T age;

public:
    Base(/* args */);
    Base(string name, T age);
    ~Base();
    friend void show(Base<T> &b);//show非模板函数,只是使用一个模板作为参数,这意味着必须要为使用的友元定义显式具体化
    // {
    //     cout << b.name << b.age << endl; //如果需要访问Base中的name和age,需要编程Base的友元
    // }
};

template <class T>
Base<T>::Base(/* args */)
{
}
template <class T>
Base<T>::Base(string name, T age)
{
    this->name = name;
    this->age = age;
}

template <class T>
Base<T>::~Base()
{
}

void show(Base<int> &b)//具显化使用
{
    cout << b.name << b.age << endl; //如果需要访问Base中的name和age,需要编程Base的友元
}

void test()
{
    Base<int> b("zhangsan", 20);
    show(b);
}

int main(int argc, char const *argv[])
{
    test();
    system("pause");
    return 0;
}
2.友元函数模板

当前只能使用模板类的非约束模板友元函数。

#include <iostream>
using namespace std;
template <class T>
class HasFriend
{
private:
    string name;
    T age;

public:
    HasFriend(string name, T age);
    template <class S>
    friend void count();
    template <class S>
    friend void report(HasFriend<S> &h);//友元函数不要使用与HasFriend相同class T,会有警告,甚至可能造成编译错误
};

template <class T>
HasFriend<T>::HasFriend(string name, T age)
{
    this->name = name;
    this->age = age;
}

template <class S>
void count()
{
    cout << sizeof(HasFriend<S>) << endl;
}

template <class T>
void report(HasFriend<T> &h)
{
    cout << h.name << h.age << endl;
}

void test()
{
    HasFriend<int> has("zhangsan", 30);
    count<int>();
    report(has);
}

int main(int argc, char const *argv[])
{
    test();
    system("pause");
    return 0;
}

注意:<>不能再使用了,在以前,它有两层意思,一是,表明此友元函数是函数模板; 二是,此模板使用的模板类型参数为当前 模板类的类型参数class,但是不能使用了,请使用上面那种

下面这种是之前的写法:模板的约束模板友元函数 不能在使用了。

#include <iostream>
using namespace std;
template <class T>
class Test
{
private:
    T num;

public:
    Test(T n)
    {
        num = n;
    }
    friend ostream &operator<<<>(ostream &out, const Test<T> &);
};
template <class T>
ostream &operator<<<>(ostream &out, const Test<T> &obj)
{
    out << obj.num;
    return out;
};
void main()
{
    Test<int> t(414);
    cout << t;
}

14.类模板的静态成员

类中的static大家应该都清楚,只会初始化一次,后续都是赋值,不改变内存空间地址。

但是类模板中,编译器采用了二级编译机制,编译器遇到类模板,不会编译泛型,只在执行时确定了泛型类型,在编译。

这相当于在程序中每遇到一种具体的类就编译一次

注意:最后结果就是每个类模板实例化的类都有属于他们自己的static成员。这句话测试是错误的,不是每个类模板都有属于自己的static成员,而是

相同类型的,有共有的static成员,非相同类型的,有各自的static成员。

#include <iostream>
using namespace std;

template <typename T>
class Obj
{
public:
    static T m_t;
};

template <typename T>
T Obj<T>::m_t = 0;

int main()
{
    Obj<int> i1, i2, i3;
    i1.m_t = 10;
    i2.m_t++;
    i3.m_t++;
    cout << Obj<int>::m_t << endl;
    cout << i1.m_t << endl;//12

    Obj<float> f1, f2, f3;//float类型,static m_t有新的成员
    f1.m_t = 10;
    f2.m_t++;
    f3.m_t++;
    cout << Obj<float>::m_t << endl;//12.0

    Obj<char> c1, c2, c3;c
    c1.m_t = 'a';
    c2.m_t++;
    c3.m_t++;
    cout << Obj<char>::m_t << endl;//c
    cout << i1.m_t << endl; //12
    cout << Obj<int>::m_t << endl;//12,类也是如此
    system("pause");
    return 1;
}

12
12
12
c
12
请按任意键继续. . .

建议:不推荐使用类模板的静态成员,很容易出问题。

};
void main()
{
Test t(414);
cout << t;
}


### 14.类模板的静态成员

**类中的static大家应该都清楚**,只会初始化一次,后续都是赋值,不改变内存空间地址。

但是**类模板**中,编译器采用了二级编译机制,编译器遇到类模板,不会编译泛型,只在执行时确定了泛型类型,在编译。

**这相当于在程序中每遇到一种具体的类就编译一次**,

注意:**最后结果就是每个类模板实例化的类都有属于他们自己的static成员。这句话测试是错误的,不是每个类模板都有属于自己的static成员,而是**

**相同类型的,有共有的static成员,非相同类型的,有各自的static成员。**

```c++
#include <iostream>
using namespace std;

template <typename T>
class Obj
{
public:
    static T m_t;
};

template <typename T>
T Obj<T>::m_t = 0;

int main()
{
    Obj<int> i1, i2, i3;
    i1.m_t = 10;
    i2.m_t++;
    i3.m_t++;
    cout << Obj<int>::m_t << endl;
    cout << i1.m_t << endl;//12

    Obj<float> f1, f2, f3;//float类型,static m_t有新的成员
    f1.m_t = 10;
    f2.m_t++;
    f3.m_t++;
    cout << Obj<float>::m_t << endl;//12.0

    Obj<char> c1, c2, c3;c
    c1.m_t = 'a';
    c2.m_t++;
    c3.m_t++;
    cout << Obj<char>::m_t << endl;//c
    cout << i1.m_t << endl; //12
    cout << Obj<int>::m_t << endl;//12,类也是如此
    system("pause");
    return 1;
}

12
12
12
c
12
请按任意键继续. . .

建议:不推荐使用类模板的静态成员,很容易出问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值