【C++第三阶段】模板类模板通用数组实现案例

以下内容仅为当前认识,可能有不足之处,欢迎讨论!


文章目录

  • 模板
    • 怎么使用模板
    • 函数模板注意事项
    • 普通函数与函数模板的区别
    • 普通函数与函数模板调用规则
    • 函数模板限制
  • 类模板
    • 类模板语法
    • 类模板与函数模板区别
    • 类模板中成员函数创建时机
    • 类模板对象做函数参数
      • 打印类别
    • 类模板与继承
    • 类模板成员函数类外实现
    • 类模板分文件编写
    • 类模板与友元
    • 类模板案例-通用数组实现


本教程针对泛型编程和STL做技术讲解,探讨更深层的使用。

模板

什么是模板,模板的目的是什么?怎么用模板,优缺点,能用在哪里。

C++提供两种模板机制,函数模板和类模板。

什么是模板?

建立通用的模具,提高复用性。

模板的目的是什么?

提高代码的复用性,同时解决函数重载的问题:不能根据情况指定不同的返回值类型。以及其他函数重载的问题(2024年3月26日20点44分暂时想不到了)

黑马:建立通用函数,其函数返回值类型和形参类型可以不具体指定,用一个虚拟的类型代表。

怎么使用模板

首先,明确模板的语法如下:

template<typename T>
//后写方法

其中

template 是创建模板的声明,说明下面接着的方法用到了模板。

typename是传入不同类型的参数,这个类型是typename。

黑马:typename-表明其后符号是一种数据类型,可以用class类型。

T是通用数据类型,名称可以替换,通常为大写字母。

接着是模板使用具体案例:

#include<iostream>
#include<string>
using namespace std;//以下是模板的简单举例
//说明返回值是temp。
template <typename T>
T MySwap(T& param1, T& param2) {T temp = param1;param1 = param2;param2 = temp;return temp;
}void test0326a() {int a = 10;int b = 20;cout << "now , a = " << a << " , b = " << b << " . " << endl;//隐式调用模板函数MySwap(a, b);cout << "After implicitly utilizing swap func , a= " << a << " , b = " << b << " . " << endl;//显式调用模板函数MySwap<int>(a, b);cout << "After explicitly utilizing swap func , a = " << a << " , b = " << b << " . " << endl;
}int main() {test0326a();system("pause");return 0;
}

可以看到,有两种使用模板方式,隐式及显式。隐式直接传入参数即可,由编译器自行判断参数是何数据类型。显式需要在调用时在函数名与参数列表中用<>说明是何数据类型。

运行结果:

image-20240326231120001

函数模板注意事项

隐式:自动类型推导,必须推导出一致的数据类型T,才可以使用。

显式:模板必须要确定T的数据类型,才可以使用。

注意事项一:必须推导出一致的数据类型T才可以使用。比如下面的将C作为参数传入,识别出两个数据类型不一致。

image-20240326214133982

注意事项二:既然用了函数模板,就需要确定T的数据类型。

像下面的函数,如果隐式地让编译器自动确定数据类型,编译器不知道要确定哪个数据类型。就会报错。所以需要显式地指定数据类型。

image-20240326214501054

image-20240326214454022

所以,解决办法就是显式地指定数据类型。

void test0326c() {print<float>();
}

对应代码:

#include<iostream>
#include<string>
using namespace std;//以下是模板的简单举例
//说明返回值是temp。
template <typename T>
T MySwap(T& param1, T& param2) {T temp = param1;param1 = param2;param2 = temp;return temp;
}template <typename T>
void print() {cout << "print 函数运行." << endl;
}void test0326b() {int a = 10;int b = 20;char c = 'c';cout << "原来的a = " << a << " , b = " << b << " . " << endl;//MySwap(a, c);
}void test0326c() {print<float>();
}int main() {test0326c();system("pause");return 0;
}

运行结果:

image-20240326231048933

一个通用模板排序数组的案例代码:

#include<iostream>
#include<string>
using namespace std;template<typename T>
void MySwap(T &param1 , T &param2){T temp = param1;param1 = param2;param2 = temp;
}template<typename T>
void print(T& array, int number) {//cout << "当前数组为:";for (int i = 0; i < number; i++) {if (array[i] != '\0') {cout << array[i] << "\t";}}cout << endl;
}template <typename T>
void sort(T& array) {int member_number = sizeof(array) / sizeof(array[0]);print(array, member_number);for (int i = 0; i < member_number; i++) {for (int j = i; j < member_number-1; j++) {if (array[i] > array[j + 1]) {MySwap(array[i], array[j + 1]);}}}print(array, member_number);
}int main() {int int_array[] = { 2,9 ,3, 5 , 1 };float float_array[] = { 3.1 , 9.1 , 3.6 , 5.2 };char char_array[] = "fezcda";sort(char_array);sort(int_array);sort(float_array);system("pause");return 0;}

运行结果:

image-20240326230951333

普通函数与函数模板的区别

区别:是否会发生隐式类型转换。

A—普通函数传入参数时,会发生隐式类型转换。

B—a函数模板传入参数,如果是隐式地自动类型推导,不会发生隐式类型转换,会发生报错。

B—b函数模板传入参数,如果是显式地指定类型方式,会发生隐式类型转换。

根据以上三个编号,分别设计代码验证。

#include<iostream>
#include<string>
using namespace std;//设计简单的加法函数与加法重载
int MyAdd(int a, int b) {//如果是引用方式就不可以return a + b;
}template<typename T>
T MyTempAdd(T a, T b) {//如果是引用方式就不可以return a + b;
}int main() {int int_array[] = { 2,9 ,3, 5 , 1 };float float_array[] = { 3.1 , 9.1 , 3.6 , 5.2 };char char_array[] = "fezcda";int a = 10;int b = 13;char c = 'c'; //ASCII = 99cout <<"myadd a+b = " << MyAdd(a, b) << endl;cout << "myadd a+c = " << MyAdd(a, c) << endl;cout << "MyTempAdd a+b = " << MyTempAdd(a, b) << endl;cout << "MyTempAdd a+c = " << MyTempAdd<int>(a, c) << endl;//必须显式指定数据类型,否则会报错。system("pause");return 0;
}

image-20240326230719245

运行结果:

image-20240326230823336

问题:这里就不能再传入引用,如果引用,必须是对应的数据类型,无论是函数还是模板。

普通函数与函数模板调用规则

1.如果函数模板和普通函数都可以实现,优先调用普通函数。

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

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

4.如果函数模板可以产生更好的匹配,优先调用函数模板。

1.优先调用普通函数

#include<iostream>
#include<string>
using namespace std;void print( int a) {cout << "调用普通函数" << endl;
}template<typename T>
void print(T a) {cout << "调用函数模板" << endl;
}void test0326e() {int a = 1;print(a);
}int main() {test0326e();system("pause");return 0;
}

通过结果可以看出,优先调用了普通函数。

image-20240327000647436

2.可以通过空模板参数列表强制调用函数模板。

在前面加上空的英文书名号就可以。

更改test0326e()函数中代码为:

void test0326e() {int a = 1;print<>(a);
}

即可优先调用函数模板

image-20240327000801084

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

参数个数不同,参数类型不同。

#include<iostream>
#include<string>
using namespace std;template<typename T>
void print(T a) {cout << "调用函数模板" << endl;
}template<typename T>
void print(T a, T b) {cout << "调用重载的函数模板" << endl;
}void test0326e() {int a = 1;int b = 2;print<>(a ,b);
}int main() {test0326e();system("pause");return 0;
}

可以看到,调用了重载的函数模板。

image-20240327000930124

4.如果函数模板可以产生更好的匹配,优先调用函数模板。

怎么理解?比如下面代码,传入的是字符,在普通函数中,可以隐式转换数据类型到int,但是函数模板可以不用隐式转换直接到char。所以编译器会执行进函数重载中。

#include<iostream>
#include<string>
using namespace std;void print( int a) {cout << "调用普通函数" << endl;
}template<typename T>
void print(T a) {cout << "调用函数模板" << endl;
}void test0326e() {int a = 1;int b = 2;char c = 'c';print<>(c);
}int main() {test0326e();system("pause");return 0;
}

运行结果:

image-20240327001147736

函数模板限制

普通的数据类型,当然可以运算。

#include<iostream>
#include<string>
using namespace std;template<typename T>
bool MyCompare(T a, T b) {if (a == b) {return true;}else {return false;}
}void test0326b() {int a = 10;int b = 10;bool res = MyCompare(a, b);if (res) {cout << "a == b" << endl;}else {cout << "a != b" << endl;}
}int main() {test0326b();system("pause");return 0;
}

运行结果:

image-20240326235833087

如果是自定义的数据类型,也会出现无法运算的时候。

如果是自定义数据类型,就如果想要走特殊的通道到达对应的函数模板,则需要特殊操作。

#include<iostream>
#include<string>
using namespace std;class Person {
public:Person(int age, string name) {this->p_age = age;this->p_name = name;}public:int p_age;string p_name;
};template<typename T>
bool MyCompare(T a, T b) {if (a == b) {return true;}else {return false;}
}template<> bool MyCompare(Person p1, Person p2) {if (p1.p_age == p2.p_age && p1.p_name == p2.p_name) {return true;}else {return false;}
}void test0326b() {int a = 10;int b = 10;bool res = MyCompare(a, b);if (res) {cout << "a == b" << endl;}else {cout << "a != b" << endl;}
}void test0326d() {Person tom(12, "Cooper");Person Jack(12, "Cooper");bool res = MyCompare(tom, Jack);if (res) {cout << "a == b" << endl;}else {cout << "a != b" << endl;}
}int main() {test0326d();//test0326b();system("pause");return 0;
}

运行结果:

image-20240327000237165

类模板

类模板语法

目的/作用:

建立一个通用类,类中的成员数据类型可以不具体指定,用一个虚拟的类型来代表。

我理解:望名思义,以为是通用的类,类名也是通用的。但其实是类里面的属性或者方法,可以通用。

语法:

template <typename T1 ,typename T2 , typename ,Tn>
class{}

template 声明创建模板

typename 表明后面的符号是一种数据类型,具体是什么数据类型可以自己指定。

T 通用的数据类型,名称可以替换,通常为大写字母。

案例代码:

#include<iostream>
#include<string>
using namespace std;template<typename name_type , typename age_type>
class Person {
public:Person(name_type name, age_type age) :p_name(name), p_age(age) {}public:name_type p_name;age_type p_age;public:void print() {cout << "name = " << p_name << " , age = " << p_age << " . " << endl;}
};void test0327a() {Person <string, int>p("Json", 23);p.print();
}int main() {//test0326e();test0327a();system("pause");return 0;
}

运行结果:

image-20240327194736825

类模板与函数模板区别

区别①类模板不能自动推导类型。

函数模板可以通过传入参数,由编译器判断谁是什么数据类型,而类模板没有这个功能。

区别②类模板可以对类型赋默认值。

如果类中成员属性≥1,可以对成员属性的类型赋默认值。

以下是两点具体代码体现。

void test0327b() {Person p("docker", 99);p.print();
}

区别①会报错:

image-20240327200148738

区别②:

#include<iostream>
#include<string>
using namespace std;template<typename name_type , typename age_type = int>//体现区别2,可以对类型赋默认值。
class Person {
public:Person(name_type name, age_type age) :p_name(name), p_age(age) {}public:name_type p_name;age_type p_age;public:void print() {cout << "name = " << p_name << " , age = " << p_age << " . " << endl;}
};
}void test0327b() {//Person p("docker", 99);//p.print();
}void test0327c() {Person <string>p("Coco", 33);//体现2,可以对类型赋默认值,使得string数据类型是参数1,默认数据类型是参数2p.print();
}int main() {test0327c();system("pause");return 0;
}

运行结果:

image-20240327200302791

类模板中成员函数创建时机

类模板成员函数和普通类中成员函数创建时机有区别。

  • 普通类成员函数一开始创建。
  • 类模板成员函数调用时创建。

问题:什么叫调用时才创建?

因为具体的类有明确的数据类型,而类模板没有指定的数据类型,所以一开始时不会创建,在实际调用时才会创建。

代码示例:

#include<iostream>
#include<string>
using namespace std;class Per {
public:void print() {cout << "Per类调用print函数" << endl;}
};class Son {
public:void show() {cout << "Son类调用show函数" << endl;}
};template<class T>
class Person {
public:T obj;//这一点在自己编写时忘记了
public:void p_print(){obj.print();}void p_show() {obj.show();}
};void test0327d() {Person <Per> per;per.p_print();//per.p_show();Person <Son> son;//son.p_print();son.p_show();
}
int main() {test0327d();system("pause");return 0;
}

如果不注释per.p_print();son.p_print();,就会报错:image-20240327202523442

注释掉之后,能有运行结果:

image-20240327202555263

类模板对象做函数参数

掌握:类模板实例化出的对象,向函数传参的方式

什么意思?就是将对象作为参数传入函数中。但此时的对象是类模板对象,所以又有模板数据类型不确定的问题在里面。就会有对应三种类模板对象做函数参数的方法。

三种传入方式:

①指定传入类型——直接显示对象的数据类型。

②参数模板化——将对象中的参数变为模板进行传递。

③整个类模板化——将这个对象类型模板化进行传递。

第一种方法,指定传入类型,就是在将类模板对象作为参数传入普通函数时,直截了当的在函数定义时就说明类模板对象的模板数据类型是什么。

第二种,参数模板化——将对象中的参数作为模板传递。

此时函数不再是普通函数,而是模板函数。类模板对象中的模板数据类型需要与模板函数中的模板数据类型保持一致。

第三种,整个类模板化——将这个对象类型作为模板进行传递。

示例代码:

#include<iostream>
#include<string>
using namespace std;template<class T1 , class T2>
class Person {
public:Person(T1 name, T2 age):p_name(name),p_age(age) {}public:T1 p_name;T2 p_age;public:void print() {cout << "name = " << p_name << " , age = " << p_age << " . " << endl;}
};//方式1:指定类模板对象模板数据类型,把类模板对象作为参数传入函数中。
void CommonPrintPerson(Person<string , int> &p) {//现在这是个普通函数p.print();
}//方式2:参数模板化,使用模板函数,把类模板对象中的成员属性类型作为模板数据类型传入。
template<typename T1 , typename T2>
void TemplatePrintPerson(Person <T1, T2>& p) {p.print();
}//方式3:对象模板化,将类模板对象也作为模板数据类型传入函数中
template <class T3>
void TemplateClassPrintPerson(T3 p) {p.print();
}void test0327e() {Person<string, int>p("Jack", 22);Person<string, int>e("Eoa", 32);Person<string, int>r("Ross", 32);//普通函数调用时,指定模板数据类型将类模板对象作为参数传入函数中。CommonPrintPerson(p);TemplatePrintPerson<string, int>(e);TemplateClassPrintPerson(r);
}int main() {test0327e();system("pause");return 0;
}

运行结果:

image-20240327210559275

打印类别

补充的一点是可以通过typeid(要查看的数据类型).name()获得是什么类别。

比如

类模板与继承

当类模板碰到继承时,需要注意以下几点:

如果子类继承的父类是一个类模板时,子类在声明时,需要指定父类中T的类型。如果不指定,编译器无法给子类分配内存。

如果想灵活指定父类中T的类型,子类也需要变为类模板。

为什么不指定父类T中的类型,编译器无法给子类分配内存呢?因为不确定父类有什么数据类型。

所以,对于第一点显式指定父类数据类型,指定的模板数据类型需要写在父类的右边。

image-20240327213010633

如果想要灵活指定父类中的模板数据类型,子类也需要变为类模板。

代码示例如下:

#include<iostream>
#include<string>
using namespace std;template <class T>
class Base {
public:T member;
};//显式指定父类模板数据类型
class Under :public Base<int> {
public:Under() {cout << "父类继承子类,显式指定类模板数据类型" << endl;cout << "类模板数据类型为:" << typeid(member).name() << "."<<endl;}};//灵活指定父类模板,需要子类也是类模板
template <class T1 , class T2>
class Bottom :public Base<T2> {
public:Bottom() {cout << "灵活指定父类模板,需要子类也是类模板" << endl;//cout << "父类类模板数据类型为:" << typeid(member).name() << "." << endl;//需要注释掉才能运行,说明子类先构造,父类才构造。而显式指定的已经指定了。cout << "子类类模板数据类型为:" << typeid(B_member).name() << "." << endl;}
public:T2 B_member;
};void test0327f() {Under u;Bottom<int, char>bo;
}int main() {test0327f();system("pause");return 0;
}

运行结果:

image-20240327213707604

类模板成员函数类外实现

学习目标:掌握类模板外的成员函数类外实现

成员函数类外实现,需要加上类模板的说明,同时加上类模板参数。

代码说明:

#include<iostream>
#include<string>
using namespace std;template<class T1 , class T2 >
class Person {
public:Person(T1 name , T2 age);
public:T1 p_name;T2 p_age;
public:void show();
};template<class T1 , class T2>
Person<T1, T2>::Person(T1 name, T2 age) {this->p_name = name;this->p_age = age;
}template<class T1 , class T2>
void Person<T1 , T2>::show() {cout << "name = " << this->p_name << " , age = " << this->p_age << " . " << endl;
}void test0327g() {Person <string, int>p("Ross", 33);p.show();
}int main() {test0327g();system("pause");return 0;
}

运行结果:

image-20240327221140056

类模板分文件编写

问题:类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到。

解决:

①直接包含cpp源文件。

②将声明和实现写入同一个文件中,并改后缀名为.hpp,约定名称,不强制。

类模板中的成员函数创建时机是在调用阶段,调用才会创建,一开始不创建。所以要么包含源码—cpp文件,要么写入同一个文件中,后缀名是’hpp’。

防止头文件重复包含

类模板与友元

学习目标:掌握类模板配合友元函数的类内和类外实现

全局函数类内实现👉直接在类内声明友元即可。

全局函数类外实现👉需要提前让编译器知道全局函数的存在。

我的理解:加了友元标识符之后,类内的函数就变成了全局函数。所以可以在类外直接调用。

弹幕:printPerson函数不加friend时是一个私有的成员函数,但加了friend之后就变成了一个全局函数,因为自身的成员函数默认就可以调用自身的成员属性不需要友元,反之需要友元的都不是成员函数。

全局函数类内实现就一步到位。

代码示例:

#include<iostream>
#include<string>
using namespace std;template<class T1 , class T2>
class Person {//类内全局函数friend void printPerson(Person<T1,T2> p) {cout << "全局函数类内实现" << endl;cout << "name = " << p.p_name << " , age = " << p.p_age << " . " << endl;}
public:Person(T1 name, T2 age) :p_name(name), p_age(age) {}
private:T1 p_name;T2 p_age;
};void test0327h() {Person <string, int> p("张三", 34);printPerson(p);
}int main() {test0327h();system("pause");return 0;
}

运行结果:

image-20240327231909161

全局函数类外实现,由于模板函数的特殊,若将类外模板函数的实现写于声明后,编译器不认识。并且,类内的函数声明是普通的函数声明,需要将其转换成模板函数的声明,

image-20240327232647811

如图,是全局函数类外实现写在了声明后,编译器不认识。

如果写在声明前,由于传入了Person类,编译器也不认识Person类,还需要把Person类的声明也写在前面。

image-20240327232355135

接下来,还是不行,因为这样相当于全局函数是普通函数,需要加上空模板参数列表让编译器认为它是函数模板。

最后,终于成功生成。

image-20240327232714203

完整代码:

#include<iostream>
#include<string>
using namespace std;template<class T1, class T2>
class Person;template<class T1, class T2>
void showPerson(Person<T1, T2> p) {cout << "全局函数类外实现" << endl;cout << "name = " << p.p_name << " , age = " << p.p_age << " . " << endl;
}template<class T1 , class T2>
class Person {//类内全局函数friend void printPerson(Person<T1,T2> p) {cout << "全局函数类内实现" << endl;cout << "name = " << p.p_name << " , age = " << p.p_age << " . " << endl;}friend void showPerson<>(Person <T1, T2> p);
public:Person(T1 name, T2 age) :p_name(name), p_age(age) {}
private:T1 p_name;T2 p_age;
};void test0327h() {Person <string, int> p("张三", 34);//printPerson(p);showPerson(p);
}int main() {test0327h();system("pause");return 0;
}

运行结果:

image-20240327232732006

看弹幕:友元不是类的成员,也不接受它所在区域访问控制级别的约束。

所以,一般都是全局函数类内实现。当然,也可以直接在类里面写上函数模板。

template<class T1 , class T2>
friend void showPerson(Person <T1, T2> p);

一样可以运行。

类模板案例-通用数组实现

制作一个通用的数组类,可以满足如下要求:

  1. 对内置数据类型以及自定义数据类型的数据进行存储。
  2. 将数组中的数据存储到堆区。
  3. 构造函数中可以传入数组的容量。
  4. 提供对应的拷贝构造函数以及operator=防止浅拷贝问题。
  5. 提供尾插法和尾删法对数组中的数据进行增加和删除。
  6. 可以通过下标的方式访问数组中的元素。
  7. 可以获取当前数组中的元素个数和数组的容量。

自己的分析:

①,可以用类模板存储元素。

②,在每个数据存储时,也就是构造函数,需要new一个数据到堆区。同时析构函数需要释放内存。

③,怎么理解?——传入数组容量,才能确定开辟多少内存空间。

④,这个拷贝构造函数,说的是类的拷贝。重写=,就是在类中重写=号运算符。

⑤,尾插法,尾删法。提供两种方法,参数是什么?对于插入,参数就是新的数据元素;对于删除,可以没有参数。内存空间的扩充怎么做?参考职工管理系统。内存空间的删除,就是直接将最后一个元素内存地址释放,并将数组个数-1。

⑥传入对应的数字,获取其东西。

CommonArray.hpp

#include<iostream>
#include<string>
using namespace std;当前元素个数和数组容量。
*/
template <class T>
class CommonArray;template<class T>
class CommonArray {
public:CommonArray(int num);CommonArray(CommonArray& ca);~CommonArray();
public://这个代表什么意思?为什么要是创建一个T类型的东西?这个东西再指向一个数组?T* array;
private:int ca_num;int ca_size;
public:void tail_insert(const T &element);void tail_delete();int get_array_size();int get_element_number();int get_common_array_cost();CommonArray& operator=(const CommonArray& ca);T& operator[](int index);
};template<class T>
CommonArray<T>::CommonArray(int size) {//cout << "有参构造函数调用" << endl;//容量:数组内一共可以放ca_num个元素。this->ca_size = size;//新建一个T类型的数组指针,并把地址设为null//this->array = NULL;//开辟这么大一个空间的数组。this->array = new T[this->ca_size];this->ca_num = 0;
};template<class T>
CommonArray<T>::CommonArray(CommonArray& ca) {//cout << "拷贝构造函数调用" << endl;this->ca_size = ca.ca_size;this->ca_num = ca.ca_num;this->array = new T[ca.ca_size];for (int i = 0; i < ca.ca_size; i++) {this->array[i] = ca.array[i];}
}//拷贝构造函数和重写=运算符防止编译器提供的两个函数造成浅拷贝现象。
//如果浅拷贝,删除时会导致堆区数据重复释放。template<class T>//返回值左值存在,应该要返回引用。
CommonArray<T>& CommonArray<T>::operator=(const CommonArray<T>& ca) {//cout << "operator=函数调用" << endl;this->ca_size = ca.ca_size;this->ca_num = ca.ca_num;this->array = new T[ca.ca_size];for (int i = 0; i < ca.ca_size; i++) {this->array[i] = ca.array[i];}return *this;
}template<class T>
CommonArray<T>::~CommonArray() {if (this->array) {delete[] this->array;this->array = 0;this->ca_size = 0;this->ca_num = 0;}
}//为了防止T被修改,一般用引用的方式传入,使用const
template<class T>
void CommonArray<T>::tail_insert(const T &element) {if (this->ca_num != this->ca_size) {this->array[this->ca_num] = element;this->ca_num += 1;}else {cout << "数组已满,无法新增。" << endl;system("pause");return;}
}template<class T>
void CommonArray<T>::tail_delete() {this->ca_num--;
}template<class T>
int CommonArray<T>::get_array_size() {return this->ca_size;
}template<class T>
int CommonArray<T>::get_element_number() {return this->ca_num;
}template<class T>
int CommonArray<T>::get_common_array_cost() {int cost;cost = sizeof(T) * this->ca_size;return cost;
}template<class T>
//返回值是T是因为要作为返回值,又因为想让它作为左值存在,比如int i = 0 ,就需要让他放回一个引用。。
T& CommonArray<T>::operator[](int index) {return this->array[index];
}

测试文件.cpp

#include<iostream>
#include<string>
using namespace std;#include"CommonArray.hpp"class Person {
public:Person() {}Person(string name, int age) :p_name(name),p_age(age) {}
public:string p_name;int p_age;
};void Print_Array(CommonArray<int>& common_array) {for (int i =0 ; i< common_array.get_element_number(); i++){cout << common_array.array[i] << " ";}
}void Print_Person_Array(CommonArray<Person>& common_array_persons) {for (int i = 0; i < common_array_persons.get_element_number(); i++) {cout << "数组成员" << i + 1 << ": 姓名:" << common_array_persons.array[i].p_name<<"\t,";cout << "\t年龄:" << common_array_persons.array[i].p_age << " \t. " << endl;}}void test0328() {CommonArray<int> ca(5);for (int i = 0; i < ca.get_array_size(); i++) {ca.tail_insert(i);}cout << "ca原先为:" ;Print_Array(ca);ca.tail_delete();cout << "现在的ca容量大小为:" << ca.get_array_size() << endl;cout << "现在的ca 元素个数为:" << ca.get_element_number() << endl;cout << "ca现在为:";Print_Array(ca);cout << endl;CommonArray<Person> persons(5);Person p1("孙悟空", 999);Person p2("韩信", 19);Person p3("爪云", 39);Person p4("张飞", 29);Person p5("安其拉", 18);persons.tail_insert(p1);persons.tail_insert(p2);persons.tail_insert(p3);persons.tail_insert(p4);persons.tail_insert(p5);Print_Person_Array(persons);}int main() {test0328();system("pause");return 0;
}

image-20240402203442273


以上是我的学习笔记,希望对你有所帮助!
如有不当之处欢迎指出!谢谢!

学吧,学无止境,太深了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/589567.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java运行时内存

从jdk1.7以及以后&#xff0c;静态变量和常量池存在堆空间。

蓝桥杯算法题:区间移位

题目描述 数轴上有n个闭区间&#xff1a;D1,...,Dn。 其中区间Di用一对整数[ai, bi]来描述&#xff0c;满足ai < bi。 已知这些区间的长度之和至少有10000。 所以&#xff0c;通过适当的移动这些区间&#xff0c;你总可以使得他们的“并”覆盖[0, 10000]——也就是说[0, 100…

前端作业之完成学校官方网页的制作

&#xff08;未使用框架&#xff0c;纯html和css制作&#xff09; 注&#xff1a;由本人技术限制&#xff0c;代码复用性极差 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>xxx大学</tit…

Bert基础(十二)--Bert变体之知识蒸馏原理解读

B站视频&#xff1a;https://www.bilibili.com/video/BV1nx4y1v7F5/ 白话知识蒸馏 在前面&#xff0c;我们了解了BERT的工作原理&#xff0c;并探讨了BERT的不同变体。我们学习了如何针对下游任务微调预训练的BERT模型&#xff0c;从而省去从头开始训练BERT的时间。但是&#…

海外媒体软文发稿:带动海外宣发新潮流,迈向国际舞台

引言 随着全球化的发展&#xff0c;越来越多的中国企业希望在国际舞台上展示自己的实力。而海外媒体软文发稿作为一种全新的海外宣传方式&#xff0c;正逐渐成为带动海外宣发新潮流的有力工具。本文将探讨海外媒体软文发稿的优势和如何迈向国际舞台。 海外媒体软文发稿的优势…

如何在本地搭建集成大语言模型Llama 2的聊天机器人并实现无公网IP远程访问

文章目录 1. 拉取相关的Docker镜像2. 运行Ollama 镜像3. 运行Chatbot Ollama镜像4. 本地访问5. 群晖安装Cpolar6. 配置公网地址7. 公网访问8. 固定公网地址 随着ChatGPT 和open Sora 的热度剧增,大语言模型时代,开启了AI新篇章,大语言模型的应用非常广泛&#xff0c;包括聊天机…

【芯片验证】通关寄存器与ral_model —— 寄存器生成流程中加入backdoor后门配置

前言 【芯片验证】通关寄存器与ral_model —— backdoor后门访问实操测试-CSDN博客 上一篇文章中,我们通过在环境中配置后门路径的方式来实现了寄存器的后门访问,但是在实际应用中,无论寄存器RTL文件、例化还是寄存器模型大概率都是工具生成的,比如在本专栏中实现的gen_r…

GT收发器64B66B设计(1)IP核配置和example design

文章目录 前言一、IP核配置1.1、编码方式1.2、字节对齐和逗号码 二、example design 前言 在前面我们基于GT收发器进行了PHY层设计&#xff0c;其中采用的编码方式为8B10B&#xff0c;为进一步提高传输效率&#xff0c;从本文开始&#xff0c;将采用基于GT高速收发器采用64B66…

【Erlang】Linux(CentOS7)安装Erlang和RabbitMQ

一、系统环境 查版本对应&#xff0c;CentOS-7&#xff0c;选择Erlang 23.3.4&#xff0c;RabbitMQ 3.9.16 二、操作步骤 安装 Erlang repository curl -s https://packagecloud.io/install/repositories/rabbitmq/erlang/script.rpm.sh | sudo bash安装 Erlang package s…

Leaflet使用多面(MultiPolygon)进行遥感影像掩膜报错解决之道

目录 前言 一、问题初诊断 1、山重水复 2、柳暗花明 3、庖丁解牛 4、问题定位 二、解决多面掩膜问题 1、尝试数据修复 2、实际修复 3、最终效果 三、总结 前言 之前一篇讲解遥感影像掩膜实现&#xff1a;基于SpringBoot和Leaflet的行政区划地图掩膜效果实战&#xff0…

C++输出格式控制

setprecision(n)可控制输出流显示浮点数的数字个数。C默认的流输出数值有效位是6&#xff0c;所以不管数据是多少&#xff0c;都只输出六位。如果setprecision(n)与setiosflags(ios::fixed)或者setiosflags(ios_base::fixed)合用&#xff0c;可以控制小数点右边的数字个数。set…

Java中线程详解

文章目录 相关概念多线程概念实现方式继承Thread类实现Runnable接口比较 常用方法线程安全产生的原因解决思想同步同步代码块同步方法Lock锁机制 死锁概念避免 状态线程间的通讯介绍方法 相关概念 并行&#xff1a;在同一时刻&#xff0c;有多个任务在多个CPU上同时执行并发&a…