【C++】模板类相关

今日面试,对“”模板类和普通类实例化时有什么区别”的回答不是很准确,故做个总结

1. 类模板

类模板描述了一组相关的类或数据类型,它们只能通过类型来区分:整数值、指向(或引用)具有全局链接的变量的指针、其他的组合。类模板尤其适用于描述通用但类型安全的数据结构。
声明一个普通的类模板:

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;
    
    return 0;
}

与函数模板不同,类模板可以同时有类型参数(如 class Elem)和表达式参数(如 unsigned Size)。表达式参数可以是:

  • 具有整型或枚举的值
  • 指向对象的指针或到对象的引用
  • 指向函数的指针或到函数的引用
  • 指向类成员函数的指针
template <class Elem> class Array {
        Elem* data;
        int size;
    public:
        Array( int sz );
        int GetSize();
        Elem& operator[]( int idx );
};

template <unsigned Size> class String {
        char data[Size];
        static int overflows;
    public:
        String( char *initial );
        int length();
};

类模板的完整定义需要类模板函数成员和静态数据成员的定义。动态(非静态)数据成员由类模板声明完全定义。

2. 模板类的继承

在模板类的继承中,需要注意以下两点:

  • 如果父类自定义了构造函数,记得子类要使用构造函数列表来初始化
  • 继承的时候,如果子类不是模板类,则必须指明当前的父类的类型,因为要分配内存空间
  • 继承的时候,如果子类是模板类,要么指定父类的类型,要么用子类的泛型来指定父类
template <typename T>
class Parent{
public:
    Parent(T p)
    {
        this->p = p;
    }
    
private:
    T p;
};

//如果子类不是模板类,需要指明父类的具体类型
class ChildOne:public Parent<int>{
    
public:
    ChildOne(int a,int b):Parent(b)
    {
        this->cone = a;
    }
    
private:
    int cone;
};


//如果子类是模板类,可以用子类的泛型来表示父类
template <typename T>
class ChildTwo:public Parent<T>{
    
public:
    ChildTwo(T a, T b):Parent<T>(b)
    {
        this->ctwo = a;
    }
    
private:
    T ctwo;
};

3. 模板函数和友元模板函数

普通模板函数和友元模板函数,声明和定义都写在类的内部,也不会有什么报错。正常。

template <typename T>
class Complex {
    
    //友元函数实现运算符重载
    friend ostream& operator<<(ostream &out, Complex &c)
    {
        out<<c.a << " + " << c.b << "i";
        return out;
    }
    
public:
    Complex(T a, T b)
    {
        this->a = a;
        this->b = b;
    }
    
    //运算符重载+
    Complex operator+(Complex &c)
    {
        Complex temp(this->a + c.a, this->b + c.b);
        return temp;
    }
    
    //普通加法函数
    Complex myAdd(Complex &c1, Complex &c2)
    {
        Complex temp(c1.a + c2.a, c1.b + c2.b);
        return temp;
    }
    
private:
    T a;
    T b;
};

int main()
{
    Complex<int> c1(1,2);
    Complex<int> c2(3,4);
    
    Complex<int> c = c1 + c2;
    
    cout<<c<<endl;
    
    return 0;
}

如果普通的模板函数声明在内的内部,定义在类的外部,不管是否处于同一个文件,就跟普通的函数一样,不会出现任何错误提示。但是如果是友元函数就会出现报错,是因为有二次编译这个机制存在。
在编译器进行编译的时候,编译器会产生类的模板函数的声明,当时实际确认类型后调用的时候,会根据调用的类型进行再次帮我们生成对应类型的函数声明和定义。我们称之为二次编译。同样,因为这个机制,会经常报错找不到类的函数的实现。在模板类的友元函数外部定义时,也会出现这个错误。解决方法是 “ 类的前置声明和函数的前置声明 ”。


#include <iostream>
using namespace std;


template <typename T>
class Complex {
    
    //友元函数实现运算符重载
    friend ostream& operator<<(ostream &out, Complex<T> &c);
    
public:
    Complex(T a, T b);
    
    //运算符重载+
    Complex<T> operator+(Complex<T> &c);
    
    //普通加法函数
    Complex<T> myAdd(Complex<T> &c1, Complex<T> &c2);
    
private:
    T a;
    T b;
};

//友元函数的实现
template <typename T>
ostream& operator<<(ostream &out, Complex<T> &c)
{
    out<<c.a << " + " << c.b << "i";
    return out;
}


//函数的实现
template <typename T>
Complex<T>::Complex(T a, T b)
{
    this->a = a;
    this->b = b;
}

template <typename T>
Complex<T> Complex<T>::operator+(Complex<T> &c)
{
    Complex temp(this->a + c.a, this->b + c.b);
    return temp;
}

template <typename T>
Complex<T> Complex<T>::myAdd(Complex<T> &c1, Complex<T> &c2)
{
    Complex temp(c1.a + c2.a, c1.b + c2.b);
    return temp;
}


int main()
{
    Complex<int> c1(1,2);
    Complex<int> c2(3,4);
    
    Complex<int> c = c1 + c2;
    
    cout<<c<<endl;
    
    return 0;
}

友元函数的定义写在类的外部–错误信息

Undefined symbols for architecture x86_64:
  "operator<<(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, Complex<int>&)", referenced from:
      _main in demo1.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

可以通过前置声明解决二次编译的问题。

  • 类的前置声明
  • 友元模板函数的前置声明
  • 友元模板函数声明需要增加泛型支持
    在这里插入图片描述

4. 模板类的实例化

4.1 隐式实例化

4.2 显式实例化

posted @   Coputing  阅读(52)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示