C++梁哥笔记day19 | 我的日常分享

C++梁哥笔记day19

一、类模板(续day18)

1.1 类模板的成员函数在类外实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
template<class T1,class T2>
class Person{
public:
T1 name;
T2 age;
public:
//类内声明
Person(T1 name,T2 age);
void showPerson(void);
};
//类外定义
template<class T1, class T2>
Person<T1,T2>::Person(T1 name, T2 age)
{
this->name = name;
this->age = age;
cout<<"有参构造"<<endl;
}
template<class T1, class T2>
void Person<T1,T2>::showPerson()
{
cout<<"name:"<<this->name<<",age:"<<this->age<<endl;
}
void test01(){
Person<string,int> ob1("小花",18);
ob1.showPerson();
}

运行结果:

image-20210118230129284

1.2 类模板源文件与头文件分离

1.2.1 源文件与头文件分离产生的问题

perosn.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef PERSON_H
#define PERSON_H

template<class T1,class T2>
class Person
{
public:
T1 name;
T2 age;
public:
Person();
Person(T1 name,T2 age);
void showPerson();
};

#endif // PERSON_H

person.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include "person.h"
#include <iostream>
using namespace std;

template<class T1,class T2>
Person<T1,T2>::Person()
{
cout<<"无参构造函数"<<endl;
}

template<class T1,class T2>
Person<T1,T2>::Person(T1 name,T2 age)
{
this->name = name;
this->age = age;
cout<<"有参构造函数"<<endl;
}

template<class T1,class T2>
void Person<T1,T2>::showPerson()
{
cout<<"name:"<<this->name<<",age:"<<this->age<<endl;
}

main.cpp

1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include "person.h"
using namespace std;

int main(int argc, char *argv[])
{
Person<string,int> ob1("小花",18);
ob1.showPerson();
return 0;
}

运行结果:

image-20210118232621301

结果分析:因为类模板会进行两次编译,第一次是类模板本身编译,第二次是在类模板的成员调用的时候类型确定了,会以确定的类型再一次进行编译。由于C++/C 是独立文件编译的。如果类模板的.cpp和.h分文件,会造成第二次编译出错,找不到在.cpp中对类的定义。解决办法如下。

1.2.2 解决办法

  • 1、将main.cpp中的include<person.h>替换为include<person.cpp>,因为 .cpp中也包含的.h,相当于都有了,没有分离。但是include标准是包含头文件,基本不会包含.cpp

image-20210118233538852

  • 2、由于include不会包含.cpp,于是对于类模板我们不进行分离。而是将.h与.cpp中的内容都放入.h中,然后把.h的后缀改为.hpp。因为这个文件既有声明也有定义,于是我们给它起个好听的名字.hpp。然后main.cpp中包含.hpp就行。

    image-20210118234302506

person.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#ifndef PERSON_H
#define PERSON_H

template<class T1,class T2>
class Person
{
public:
T1 name;
T2 age;
public:
Person();
Person(T1 name,T2 age);
void showPerson();
};

#include <iostream>
using namespace std;

template<class T1,class T2>
Person<T1,T2>::Person()
{
cout<<"无参构造函数"<<endl;
}

template<class T1,class T2>
Person<T1,T2>::Person(T1 name,T2 age)
{
this->name = name;
this->age = age;
cout<<"有参构造函数"<<endl;
}

template<class T1,class T2>
void Person<T1,T2>::showPerson()
{
cout<<"name:"<<this->name<<",age:"<<this->age<<endl;
}

#endif // PERSON_H

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
//#include "person.h"
//#include "person.cpp"
#include "person.hpp"
using namespace std;

int main(int argc, char *argv[])
{
Person<string,int> ob1("小花",18);
ob1.showPerson();
return 0;
}

运行结果:

image-20210118234446487

1.3 类模板的强化训练

设计一个数组模板类MyArray,完成对不同类型元素的管理。

myarray.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#ifndef MYARRAY_HPP
#define MYARRAY_HPP
#include <iostream>
using namespace std;

template<class T>
class MyArray{
private:
T *addr;
int capacity;
int size;
public:
MyArray();
MyArray(int capacity);
~MyArray();
MyArray(const MyArray &ob);

//尾插法
void push_back(const T &val);
//遍历数组
void showMyArray();
};


template<class T>
MyArray<T>::MyArray()
{
this->addr = nullptr;
this->capacity = 0;
this->size = 0;
cout<<"无参构造函数"<<endl;
}

template<class T>
MyArray<T>::MyArray(int capacity)
{
this->addr = new T[capacity];
this->capacity = capacity;
this->size = 0;
cout<<"有参构造函数"<<endl;
}

template<class T>
MyArray<T>::~MyArray()
{
if(this->addr != nullptr){
delete [] this->addr;
this->addr = nullptr;
}
cout<<"析构函数"<<endl;
}

template<class T>
MyArray<T>::MyArray(const MyArray &ob)
{
this->addr = new T[ob.capacity];
//strcpy(this->addr,ob.addr);
int i = 0;
while (i<ob.size) {
this->addr[i] = ob.addr[i];
i++;
}
this->capacity = ob.capacity;
this->size = ob.size;
cout<<"拷贝构造函数"<<endl;
}

template<class T>
void MyArray<T>::push_back(const T &val)
{
if(this->size >= this->capacity){
cout<<"数组已满"<<endl;
return;
}
this->addr[this->size] = val;
this->size++;
}

template<class T>
void MyArray<T>::showMyArray()
{
int i = 0;
for(i = 0; i < this->size; i++){
cout<<this->addr[i]<<" ";
}
cout<<endl;
}

#endif // MYARRAY_HPP

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include "myarray.hpp"
#include <string>
using namespace std;
class Person{
private:
string name;
int age;
public:
friend ostream& operator <<(ostream& out,Person &ob);
Person(){
cout<<"Person默认构造函数"<<endl;
}
Person(string name,int age){
this->name = name;
this->age = age;
cout<<"Person有参构造函数"<<endl;
}
};

ostream& operator <<(ostream& out,Person &ob){
out<<"name="<<ob.name<<",age="<<ob.age;
return out;
}
void test01(){
MyArray<int> ob1(10);
ob1.push_back(1);
ob1.push_back(2);
ob1.push_back(3);
ob1.push_back(4);
ob1.showMyArray();

MyArray<string> ob2(10);
ob2.push_back("1");
ob2.push_back("b");
ob2.push_back("c");
ob2.push_back("hello world");
ob2.showMyArray();

MyArray<Person> ob3(10);
ob3.push_back(Person("小花",19));
ob3.push_back(Person("小明",15));
ob3.push_back(Person("小王",16));
ob3.showMyArray();
}

int main(int argc, char *argv[])
{
test01();
return 0;
}

运行结果:

image-20210119162417509

1.4 类模板与友元

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>

using namespace std;
template<class T1,class T2> class Person;
template<class T1,class T2> void printPerson2(Person<T1,T2> &ob);

template<class T1,class T2>
class Person{
private:
T1 name;
T2 age;
public:
Person(T1 name,T2 age);
//1、友元函数在类中定义(友元不属于该类成员函数)
friend void printPerson1(Person<T1,T2> &ob){
cout<<"name="<<ob.name<<",age="<<ob.age<<endl;
}
//2、友元函数在类外定义
friend void printPerson2<>(Person<T1,T2> &ob);
};
//2、友元函数在类外定义
template<class T1,class T2>
//注意这里是有讲究的 有了templete printPerson2是一个模板函数 在类中声明的时候需要告诉编译器(加<>) 该友元函数是一个模板函数
//而且需要将void printPerson2(Person<T1,T2> &ob)在类中声明之前,在类外在声明一次,不然编译器编译到类中声明时编译器找不到模板函数会报错
//然后这时候编译器又找不到Person类,所以还要在前面声明一下Person类
void printPerson2(Person<T1,T2> &ob){
cout<<"name="<<ob.name<<",age="<<ob.age<<endl;
}
template<class T1,class T2>
Person<T1,T2>::Person(T1 name,T2 age){
this->name = name;
this->age = age;
cout<<"Person有参构造"<<endl;
}

int main(int argc, char *argv[])
{
Person<string,int> ob("小花",18);
//通过友元 访问类模板中的数据
printPerson1(ob);
printPerson2(ob);
return 0;
}

运行结果:

image-20210119164615059

==注意:==

当友元函数在类外定义时,是有讲究的 有了templete printPerson2是一个模板函数 在类中声明的时候需要告诉编译器(加<>) 该友元函数是一个模板函数;而且需要将void printPerson2(Person<T1,T2> &ob)在类中声明之前,在类外在声明一次,不然编译器编译到类中声明时编译器找不到模板函数会报错;然后这时候编译器又找不到Person类,所以还要在前面声明一下Person类。看上面代码就懂了。