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)
二、模板(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
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; }
运行截图
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; }
运行截图