C++————4(友元重载&模板template)

一、友元重载运算符

[]、()、->不能使用友元函数重载

1.友元函数重载单目运算符

友元函数不是类的成员,没有this指针所以需要传递一个对象,他需要一个函数(obj++,obj--例外)

/*===============================================
*   文件名称:eg1_友元函数重载运算符.cpp
*   创 建 者:青木莲华
*   创建日期:2025年08月29日
*   描    述:
================================================*/
#include <iostream>
using namespace std;

class A
{
    public:
    A(){}
    A(const int num)
    {
        number = num;
    }
    void show()
    {
        cout << number << endl;
    }
    private:
    int number;
    //单目运算符    ++a前置自增
    friend A operator++(A &a)
    {   
        a.number++;
        return a;
    }
    //单目运算符 后置自增 a++       第二个参数是用来重载函数的标识,让程序知道后置++,并无实际意义
    friend A operator++(A &a,int)
    {
        A temp(a);
        a.number++;
        return temp;
    }

};




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

    A a(5);
    A b;
    b = a++;
    (a++).show();
    b.show();
    a.show();
    return 0;
}

2.友元重载双目运算符

友元函数不是类的成员,没有this指针,所以需要2个参数,而且参数顺序与运算符的操作顺序必须一致

/*===============================================
*   文件名称:eg3_友元重载双目运算符.cpp
*   创 建 者:青木莲华
*   创建日期:2025年08月29日
*   描    述:
================================================*/
#include <iostream>
using namespace std;

class A
{
    public:
    A(){}
    A(const int num)
    {
        this->num = num;
    }
    private:
    int num;
    friend bool operator==(A &a , A &b)   //左值和右值顺序对应形参顺序
    {
        return a.num == b.num ? true : false;
    }
};




int main(int argc, char *argv[])
{
    A a(11);
    A b(11);
    cout << (a == b) << endl;
    return 0;
}

3.友元函数重载标准输入输出流

/*===============================================
*   文件名称:eg2_友元函数重载标准输入输出流
*   创 建 者:青木莲华
*   创建日期:2025年08月29日
*   描    述:
================================================*/
#include <iostream>
using namespace std;

class A
{
    public:
        A(){}
    private:
        int num;
        //友元函数重载 标准输入流
        friend istream & operator>>(istream &in,A &a)
        {
            in >> a.num;
            return in;
        }
        //友元函数重载 标准输出流
        friend ostream & operator<<(ostream &out , A &a)
        {
            out << a.num;
            return out;
        }
};


int main(int argc, char *argv[])
{
    A a;
    cin >> a;
    cout << a << endl;
    return 0;
}

4.普通函数重载运算符

普通函数重载运算符:重载时,操作对象的成员必须是public权限(普通函数无法访问public外的权限)

(普通重载函数)字面量:C++11内新增了字面量的概念。

格式:

//
double operator"" xxx(long double xxx)

alt

二、模板(template)

目的:C++中模板是支持参数化多态的工具,就是让类或者函数声明为一种通用类型,使得类中的某些数据成员或者成员函数的参数,返回值在实际使用时可以是任意类型。模板也是泛型编程的基础。

模板种类:

  • 函数模板
  • 类模板

关键字:template

1.函数模板

一般形式:

//模板参数用typename和class表示
template<typename T, typename Y,......>  
返回值类型 函数名(形参列表)
{
    //函数体
}

<tips>

模板在运行过程中不会占用空间,也不再代码段内,是由编译器根据实际使用类型替换模板参数,生成实例

/*===============================================
*   文件名称:eg4_函数模板.cpp
*   创 建 者:青木莲华
*   创建日期:2025年08月29日
*   描    述:
================================================*/
#include <iostream>
using namespace std;

template<class T>
    T add(T a,T b)
{
    return a+b;
}
//多参数模板
template<class A , class B , class C>
    void fun(A a,B b,C c)
{
    cout << (a+b+c) << endl;
}

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

    //以下3个为隐式传参使用模板
    cout << add(5,6) << endl;
    cout << add('\n','a') << endl;
    //显示传递模板参数使用模板
    cout << add<double>(1.5,3.3) << endl;
    //调用多参数模板
    fun('1',100,3.14);
    return 0;
}

函数模板在使用时不需要显示说明模板参数。

3.类模板

针对数据成员函数的类型不一样的类

一般形式:

template<class X,class Y.......>
 class Demo
 {
     public:
     Demo(X x,Y y,....):x(x),y(y)....{}
     X getx()
     {
         return x;
     }
     //........
     private:
     X x;
     Y y;
 }

类模板参数

模板参数类型用class修饰的就是类型模板参数

1.函数模板声明和定义都需要 template

  • 隐式调用:自动适配 模板类型 函数名(实参列表)
  • 显式调用:自动适配 模板类型 函数名<>(实参列表)
  • 显式调用:手动执行 模板类型 函数名<类型>(实参列表)

2.类模板

template <class T>	//这里的T就叫类型模板参数

3.类模板和函数模板的关系

  • 如果一个类被设计为模板,那么他的所有成员函数都为模板
  • 如果一个成员函数为模板,这个类不一定是模板

非类型模板参数

模板的参数有些时候需要是数值,此时可以使用非类型模板参数

template<class T , int Max> 	//Max就是非类型模板参数
class Arry
{   
	public:
    
    private:
    	T arr[Max];
}

注意:非类型模板参数只支持整数如int、short、char等类型,不支持double等非整数类型,但是支持double类型指针,还支持string *

模板的默认参数

模板参数也支持设置默认参数,规则和函数默认参数一致

示例:

template<class T = char , class Y = int , int Max = 10>
class Demo
{
	public:
 	Demo(T arr,Y val):val(val)
     {
         for(int i = 0 ; i < Max ; i++)
             this->arr[i] = arr[i];
     }
 private:
 	T arr[Max];
 	Y val;
};

int main()
{
 char arr[20] = "hello";
 Demo<> obj(arr,250);		//使用默认
 Demo<char , int , 20> obj(arr,250);	//修改第3个Max
}

4.友元函数模板

友元函数模板:

  • 声明和定义在模板类中,不用加template关键字。
  • 声明和定义在模板类外!声明和定义都必须加template

alt

5.模板的实例化:

编译器在编译阶段进行实例化

6.模板的特化处理

分类:

  • 全特化——为模板的所有参数提供具体类型
template<>
class Demo<int>{};
  • 偏特化——为模板的一部分参数提供具体类型
template<class N>
class Demo<int,N>{};

练习

1、实现顺序表可以用来存储任意类型的元素并且需要重载[]、+、=、+=

源码

/*===============================================
 *   文件名称:List_template.cpp
 *   创 建 者:青木莲华
 *   创建日期:2025年08月29日
 *   描    述:链表类模板
 ================================================*/
#include <iostream>
using namespace std;

template<class L>
class List
{
    public:
        //无参构造
        List(){}
        //有参构造
        List(int size):size(size)
        {
            arr = new L[size];
            index = 0;
        }
        //拷贝构造
        List(List &list)
        {
            this->size = list.size;
            this->index = list.index;
            arr = new L[size];
            for(int i = 0 ; i < index ; i++)
                arr[i] = list.arr[i];
        }
        //插入方法
        void insert(L node)
        {
            //判满
            if(index + 1 >= size)
                return;
            arr[index] = node;
            index++;
        }
        //重载变址运算符
        L operator[](const int i)
        {
            if(i >= 0 && i < index )
            {
                return arr[i];
            }
            //假设错误处理
            L ret;
            return ret;
        }

        //重载 + 运算符 , 合并顺序表
        List operator+(const List &list)
        {
            List temp(this->size + list.size);
            temp.index = this->index + list.index;
            temp.arr = new L[this->size + list.size];
            //拷贝this->arr数组
            for(int i = 0 ; i < index ; i++)
                temp.arr[i] = this->arr[i];
            //拷贝list.arr数组
            for(int i = 0 ; i < list.index ; i++)
                temp.arr[index + i] = list.arr[i];

            return temp;
        }   
        //重载 = 运算符 右值
        List& operator=(const List &list)
        { 
            this->size = list.size;
            this->index = list.index;
            delete[] this->arr;
            arr = new L[size];
            for(int i = 0 ; i < index ; i++)
                arr[i] = list.arr[i];
            return *this;
        }
        //重载 = 运算符
        List& operator=(List &list)
        { 
            this->size = list.size;
            this->index = list.index;
            delete[] this->arr;
            arr = new L[size];
            for(int i = 0 ; i < index ; i++)
                arr[i] = list.arr[i];
            return *this;
        }
        //重载 += 运算符
        List& operator+=(List &list)
        {
            //创建中间数组
            L *temp = new L[size + list.size];
            for(int i = 0 ; i < index ; i++)
                temp[i] = arr[i];
            for(int i = 0 ; i < list.index ; i++)
                temp[index + i] = list.arr[i];
            delete[] arr;
            arr = temp;
            size += list.size;
            index += list.index;
            return *this;
        }
    private:
            L *arr;     //数组指针
            int size;   //大小
            int index;  //当前元素个数
            //友元打印重载
            friend ostream & operator<<(ostream &out,const List &list)
            {
                for(int i = 0 ; i < list.index ; i++)
                    out << list.arr[i] << " "; 
                return out;
            }
};




int main(int argc, char *argv[])
{
    List<int> list_i(10);
    list_i.insert(11);
    list_i.insert(12);
    list_i.insert(13);
    list_i.insert(14);
    list_i.insert(15);
    cout << "List<int> i : " << list_i << endl;
    cout << "i[3] = " << list_i[3] << endl;
    List<int> list_j(10);
    list_j.insert(26);
    list_j.insert(27);
    list_j.insert(28);

    List<int> list_k(10);
    // = 右值
    cout << "k = i + j : " << (list_k = list_i + list_j) << endl;
    // = &引用
    cout << "k = i : " << (list_k = list_i) << endl;
    // +=
    cout << "k += j : " << (list_k += list_j) << endl;

    List<char> ccc(10);    
    ccc.insert('a');
    ccc.insert('o');
    ccc.insert('k');
    ccc.insert('i');
    cout << "List<char> ccc : " << ccc << endl;

    List<double> ddd(10);
    ddd.insert(11.11); 
    ddd.insert(22.22); 
    ddd.insert(33.33); 
    ddd.insert(44.44); 
    ddd.insert(55.55); 
    ddd.insert(66.66); 
    cout << "List<double> ddd : " << ddd << endl;

    return 0;
}

运行截图

alt

2、实现mystring类,参考string类

源码

/*===============================================
*   文件名称:myString.cpp
*   创 建 者:青木莲华
*   创建日期:2025年08月30日
*   描    述:模拟实现string部分功能
================================================*/
#include <iostream>
#include <cstring>
using namespace std;


class mystring
{
    public:
        //无参构造
        mystring():len(0)
        {
            str = new char[maxSize];
        }
        //有参构造 常量字符串
        mystring(const char *p)
        {
            //cout << "constructor" << endl;
            len = strlen(p);
            str = new char[maxSize];
            memset(str,0,maxSize);
            for(int i = 0 ; i < len ; i++)
                str[i] = p[i];
        }
        //拷贝构造
        mystring(const mystring &obj)
        {
            //cout << "copy" << endl;
            len = obj.len;
            str = new char[maxSize];
            memset(str,0,maxSize);
            for(int i = 0 ; i < len ; i++)
                str[i] = obj.str[i];
        }
        //析构函数
        ~mystring()
        {
            if(str == nullptr)
                return;
            len = 0;
            delete[] str;
            str = nullptr;
        }
    public:
        //获取最大容量
        int max_size()
        {
            return maxSize;
        }
        //获取字符串长度
        int length()
        {
            return len;
        }
        //清空字符串
        void clear()
        {
            if(len != 0 && str != nullptr)
            {
                memset(str,0,maxSize);
                len = 0;
            }
        }
        //判空
        bool empty()
        {
            return len == 0 ? true : false;
        }
        //获取指定位置的字符
        char at(const int index)
        {
            if(index >= 0 && index < len)
            {
                return str[index];
            }
            else
            {
                cout << "out of range!" << endl;
                return 0;
            }
        }
        //append 字符串拼接常量字符串
        void append(const char *p)
        {
            int c_len = strlen(p);
            if(len + c_len >= maxSize)
                return;
            for(int i = 0 ; i < c_len ; i++)
            {
                str[len + i] = p[i];
            }
            len += c_len;
        }
        //append 字符串拼接mystring对象
        void append(const mystring &obj)
        {
            if(len + obj.len >= maxSize)
                return;
            for(int i = 0 ; i < obj.len ; i++)
                str[len + i] = obj.str[i];
            len += obj.len;
        }
    
    public: //重载
        //重载 [] 
        char operator[](const int index)
        {    
            if(index >= 0 && index < len)
            {
                return str[index];
            }
            else
            {
                cout << "out of range!" << endl;
                return 0;
            }
        }

        
        //重载 + 常量字符串
        mystring operator+(const char *p)
        {
            int c_len = strlen(p);

            mystring temp;
            temp.len = this->len + c_len;
            temp.str = new char[maxSize];

            for(int i = 0 ; i < this->len ; i++)
                temp.str[i] = this->str[i];

            for(int i = 0 ; i < c_len ; i++)
                temp.str[this->len + i] = p[i];

            return temp;
        }    
    

        //重载 + mystring对象
        mystring operator+(const mystring &obj)
        {
            mystring temp;
            temp.len = this->len + obj.len;
            temp.str = new char[maxSize];

            for(int i = 0 ; i < this->len ; i++)
                temp.str[i] = this->str[i];

            for(int i = 0 ; i < obj.len ; i++)
                temp.str[this->len + i] = obj.str[i];

            return temp;
        }    
        
         
        //重载 = 字符串常量
        mystring operator=(const char *p)
        {
            int c_len = strlen(p);

            char *temp = new char[maxSize];
            for(int i = 0 ; i < c_len ; i++)
                temp[i] = p[i];
            this->len = c_len;
            //释放旧数组资源
            delete[] this->str;
            //更改指向
            this->str = temp;
            return *this;
        }

        //重载 = mystring字符串对象
        mystring operator=(const mystring &obj)
        {
            //cout << "equals" << endl;
            char *temp = new char[maxSize];
            for(int i = 0 ; i < obj.len ; i++)
                temp[i] = obj.str[i];
            this->len = obj.len;
            //释放旧数组资源
            delete[] this->str;
            //更改指向
            this->str = temp;
            return *this;
        }
       
        
        //重载 += 字符串常量
        mystring operator+=(const char* p)
        {
            char *temp = new char[maxSize];
            
            int c_len = strlen(p);

            for(int i = 0 ; i < this->len ; i++)
                temp[i] = this->str[i];

            for(int i = 0 ; i < c_len ; i++)
                temp[this->len + i] = p[i];
            
            delete[] this->str;
        
            this->str = temp;
            this->len += c_len;
            return *this;
        }

        //重载 += mystring对象
        mystring operator+=(const mystring &obj)
        {
            char *temp = new char[maxSize];
            
            for(int i = 0 ; i < this->len ; i++)
                temp[i] = this->str[i];

            for(int i = 0 ; i < obj.len ; i++)
                temp[this->len + i] = obj.str[i];
            
            delete[] this->str;
        
            this->str = temp;
            this->len += obj.len;
            return *this;
        }
        //友元重载 << ,输出
        friend ostream& operator<<(ostream &out,const mystring &obj)
        {
            out << obj.str;
            return out;
        }

    private:
        char *str;                  //字符数组指针
        int len;                    //当前字符串字符长度
        static int maxSize;        //最大容量
};
//初始化静态变量
int mystring::maxSize = 8192;


int main(int argc, char *argv[])
{
    mystring aaa = "str1"; 
    cout << "====== mystring aaa = \"str1\" ======" << endl << aaa << endl;
    
    cout << "====== aaa[2] , aaa.at(3) ======" << endl << aaa[2] << " " << aaa.at(3) << endl;
    
    mystring *bbb = new mystring(aaa + "23");
    cout << "====== mystring *bbb = new mystring(aaa + \"23\") ======" << endl << *bbb << endl;
    
    bbb->append(aaa);
    cout << "====== bbb->append(aaa) ======" << endl << *bbb << endl;

    mystring ccc;
    cout << "====== mystring ccc; ccc.empty() ======" << endl << ccc.empty() << endl;

    ccc = "str3";
    cout << "====== ccc = \"str3\" ======" << endl << ccc << endl;

    ccc += *bbb;
    cout << "====== ccc += *bbb ======" << endl << ccc << endl;
}

运行截图

alt