【C++】C++模板知识点总结

简介: 【C++】C++模板知识点总结

function template

函数模板就是建立一个通用的函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。

template< typename T >(class和typename都行)

参数类型自动推导
    Max(x,y);
显示类型调用
    Max<int>(x,y);

template function

调用函数模板,编译器私底下会 根据传过来的变量创建对应的函数,将它具体化。——这就是通过编译生成的模板函数。


小结:

  1. 我们注意到,函数模板和函数重载似乎很类似,当他们两个并存时,如果参数类型和普通函数更匹配,就调用普通函数。
  2. 不存在普通函数,函数模板会隐式类型转换吗?不会。不提供隐式的类型转换,必须是严格的匹配。
  3. 如果显式的调用函数模板,则使用<>类型列表。
  4. 如果函数模板会产生更好的匹配,使用函数模板。
  5. 当所传参数需要隐式类型转换时,优先调用普通函数。
  6. 函数模板也可以重载。
  7. 编译器并不是把函数模板处理成能够处理任意类型的函数。
  8. 编译器从函数模板通过具体的参数类型产生不用的函数。

class template

类模板创建对象,必须显式指定类型。


类模板用于实现类所需数据的类型参数化。

类模板在表示支持多种数据结构显得特别重要,这些数据结构的表示和算法不受所包含的元素类型的影响。


类模板与继承

1.父类是一般类,子类时模板类,和普通继承类似。

2.子类是一般类,父类是模板类, 继承时必须在子类中实例化父类参数。

例如:

template <typename T>
class    A
{
public:
    A(T t = 0) :t(t)
    {

    }
    ~A()
    {

    }
    T getT()const
    {
        return this->t;
    }
private:
    T t;
};

class B:public A<int>
{
public:
    B(int b = 0) :A<int>(b)
    {

    }

private:
    int b;
};

3.父类和子类都是模板时,子类的虚拟类型可以传递到父类中。


类模板的写法注意

  1. 写在类外面时,要加参数列表, 并且要声明模板(class name< 参数列表 >)
  2. 类模板定义和声明分开写,包含主函数的.cpp 要包含"该类声明的.cpp"

书写实例
(就是基本上只要在类外写定义的有类名的位置后面都要写上< T >,除了构造析构函数,其实写上也没事,只是警告让用新的书写形式。)

(不建议使用太多的友元函数)

类模板和友元函数

在类外实现成员函数要先声明一下模板

#include<iostream>
using namespace std;


template<typename T>
class A
{
public:
    A(T a= 0);
    T& getT()const;
    A operator+(const A& other);
    void print()const;
    ~A();
    //友元函数在类中声明也要加上模板的声明
    template<typename T>
    friend A<T> addA(const A<T>& a, const A<T>& b);
private:
    T t;
};

template<typename T>
A<T>::A(T a) : t(a)
{

}
template<typename T>
A<T>::~A()
{

}
template<typename T>
A<T> A<T>::operator+(const A<T>& other)
{
    A<T> tmp;
    tmp.t = this->t + other.t;
    return tmp;
}
template<typename T>
void A<T>::print()const
{
    cout << this->t << endl;
}

template<typename T>
T& A<T>::getT()const
{
    return this->t;
}

template<typename T>
A<T> addA(const A<T>& a, const A<T>& b)
{
    A<T> temp;
    temp.t = a.t + b.t;
    return temp;
    
}

int main(void)
{
    A<int> a(100);
    a.print();
    
    A<int> b(200);
    A<int>temp = addA<int>(a,b);
    temp.print();
    return 0;
}

小结

  1. 类模板内部声明友元函数,也要声明一下模板先。
  2. 类模板成员函数内部从创建的对象别忘了加< T >。
  3. 友元函数调用,函数名后也要显示指定类型。

类模板和static数据成员

类外赋初值

template<typename T>
int A<T>::count = 666;

虚拟类型T被实例化的类型不同,所调用的内容也不同。

注意以下代码的静态变量变化即可了解。

同意类模板的虚拟类型T被实例化的类型相同,静态变量是同一个。

    A<int>a(100);
    A<int>b(200);
    a.count = 888;
    cout << a.count << endl;
    cout << b.count << endl;

    A<float>c(1.0);
    A<float>d(2.0);
    cout << c.count << endl;
    cout << d.count << endl;

静态成员数据也可以使用虚拟类型参数。

效果同上。

public:
    static T count;

小补充:

类的成员函数不能调用我们下面成员函数重载的运算符。(貌似)


类模板重载左移 在声明中加一个< T >,让编译通过。

template<typename T>
friend ostream& operator<< <T>(ostream& out, const Vector<T>& object);

(自定义)容器中放入类,该类需要有默认构造函数。

自定义容器中存放自定义类对象,该容器重载了<<,想要按照下标cout<<,那么该类也要重载<<。


练习:实现一个自定义Vector类

Vector.h

#include <iostream>

using namespace std;

template <typename  T>
class Vector
{
    template<typename T>
    friend ostream& operator<< <T>(ostream& out, const Vector<T>& object);
public:
    Vector(int size = 128); 
    Vector(const Vector& object); 
    int getLength();
    T& operator[](int index);
    Vector& operator=(const Vector& object);
    ~Vector(); /

private:
    T* m_base;
    int m_len;
};

Vector.cpp

#include <iostream>
using namespace std;
#include "Vector.h"

template<typename T>
ostream& operator<<(ostream& out, const Vector<T>& object) 
{
    for (int i = 0; i < object.m_len; i++)
    {
        out << object.m_base[i] << " ";
    }
    out << endl;

    return out;
}

template <typename T>
Vector<T>::Vector(int size)
{ 
    if (size > 0) {
        m_len = size;
        m_base = new T[m_len];
    }
}

template <typename T>
Vector<T>::Vector(const Vector<T>& object) 
{ 

    m_len = object.m_len;
    m_base = new T[m_len];
    for (int i = 0; i < m_len; i++) 
     {
        m_base[i] = object.m_base[i];
    }
}
template <typename T>
int Vector<T>::getLength() 
{
    return m_len;
}

template <typename T>
T& Vector<T>::operator[](int index) 
{
    return m_base[index];
}

template <typename T>
Vector<T>& Vector<T>::operator=(const Vector<T>& object)
{
    if (m_base != NULL) 
      {
        delete[] m_base;
        m_base = NULL;
        m_len = 0;
    }
    m_len = object.m_len;
    m_base = new T[m_len];
    for (int i = 0; i < m_len; i++) 
      {
        m_base[i] = object.m_base[i];
    }
    return *this; 
}

template <typename T>
Vector<T>::~Vector()
{ 
    if (m_base != NULL) 
    {
        delete[] m_base;
        m_base = NULL;
        m_len = 0;
    }
}

main.cpp

#include"Vector.cpp"
#include<iostream>
#include<string>
using namespace     std;


class Student {
public:
    Student() {
        age = 0;
        name = '\0';
    }

    Student(int _age, string _name):name(_name),age(_age)
    {
    }

    void print() {
        cout << name << ", " << age << endl;
    }

    ~Student() {

    }
    friend ostream& operator<<(ostream& os, const Student& stu);

private:
    string name;
    int age;
};

ostream& operator<<(ostream& os, const Student& stu)
{
    os << stu.age << " " << stu.name << endl;
    return os;
}

int main(void)
{

    
    Student s1(18, "张三");
    Student s2(18, "张四");

    Vector<Student> myStudent(2);
    myStudent[0] = s1;
    myStudent[1] = s2;
    for (int i = 0; i < myStudent.getLength(); i++)
    {
        cout << myStudent[i] << endl;
     }


    //Vector<int> myVector(10);
    //for (int i = 0; i < myVector.getLength(); i++)
    //{
    //    myVector[i] = i;
    //}
    //for (int i = 0; i < myVector.getLength(); i++)
    //{
    //    cout << myVector[i] << endl;
    //}
    //cout << "——————————————" << endl;

    //Vector<int>MyintVector(myVector);
    //for (int i = 0; i < myVector.getLength(); i++)
    //{
    //    cout << MyintVector[i] << endl;
    //}
    //cout << "——————————————" << endl;

    //Vector<float>MyFloatVector(10);
    //for (int i = 0; i < MyFloatVector.getLength(); i++)
    //{
    //    MyFloatVector[i] = i * 0.1f;
    //}
    //for (int i = 0; i < MyFloatVector.getLength(); i++)
    //{
    //    cout << MyFloatVector[i] << endl;
    //}

    //myVector = MyintVector;
    //cout << "——————————————" << endl;


    //for (int i = 0; i < myVector.getLength(); i++)
    //{
    //    cout << MyintVector[i] << endl;
    //}
    //cout << "——————————————" << endl;
    //cout << "——————————————" << endl;
    //cout << "——————————————" << endl;
    //cout << MyintVector;




    return 0;
}
相关文章
|
2月前
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
112 10
|
4月前
|
编译器 C++
【C++】——初识模板
【C++】——初识模板
【C++】——初识模板
|
22天前
|
安全 编译器 C++
【C++11】可变模板参数详解
本文详细介绍了C++11引入的可变模板参数,这是一种允许模板接受任意数量和类型参数的强大工具。文章从基本概念入手,讲解了可变模板参数的语法、参数包的展开方法,以及如何结合递归调用、折叠表达式等技术实现高效编程。通过具体示例,如打印任意数量参数、类型安全的`printf`替代方案等,展示了其在实际开发中的应用。最后,文章讨论了性能优化策略和常见问题,帮助读者更好地理解和使用这一高级C++特性。
37 4
|
23天前
|
算法 编译器 C++
【C++】模板详细讲解(含反向迭代器)
C++模板是泛型编程的核心,允许编写与类型无关的代码,提高代码复用性和灵活性。模板分为函数模板和类模板,支持隐式和显式实例化,以及特化(全特化和偏特化)。C++标准库广泛使用模板,如容器、迭代器、算法和函数对象等,以支持高效、灵活的编程。反向迭代器通过对正向迭代器的封装,实现了逆序遍历的功能。
32 3
|
5月前
|
程序员 C++
C++模板元编程入门
【7月更文挑战第9天】C++模板元编程是一项强大而复杂的技术,它允许程序员在编译时进行复杂的计算和操作,从而提高了程序的性能和灵活性。然而,模板元编程的复杂性和抽象性也使其难以掌握和应用。通过本文的介绍,希望能够帮助你初步了解C++模板元编程的基本概念和技术要点,为进一步深入学习和应用打下坚实的基础。在实际开发中,合理运用模板元编程技术,可以极大地提升程序的性能和可维护性。
|
26天前
|
编译器 C++
【c++】模板详解(1)
本文介绍了C++中的模板概念,包括函数模板和类模板,强调了模板作为泛型编程基础的重要性。函数模板允许创建类型无关的函数,类模板则能根据不同的类型生成不同的类。文章通过具体示例详细解释了模板的定义、实例化及匹配原则,帮助读者理解模板机制,为学习STL打下基础。
29 0
|
2月前
|
编译器 程序员 C++
【C++打怪之路Lv7】-- 模板初阶
【C++打怪之路Lv7】-- 模板初阶
18 1
|
2月前
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
53 0
C++入门6——模板(泛型编程、函数模板、类模板)
|
2月前
|
算法 编译器 C++
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
84 2
|
2月前
|
存储 编译器 C++
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
42 2