C++:继承、模板、CRTP:谈谈C++多态设计模式(二):类模板

本文主要从下面几个方面分析

目录

1:   类模板

2:类模板与函数模板的创建时机

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

4:类模板对象做函数参数

5:类模板与继承

6:类模板成员函数类外实现

7:类模板分文件编写

8:类模板与友元

9:类模板案列:数组类封装

1:类模板

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

语法:template<typename T>

解释:

        template -----声明创建模板

        typename ----表明后面的符号是一种数据类型,可以用class 代替

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

案列

案列描述:我们创建一个 Person类模板,含有成员变量 m_Age,m_Name。我们想让这两个成员变量的类型用一个虚拟的类型来代表,同时这个两个变量的类型可能会不一样。

解决方案:

    1:创建一个类模板,由于存在两种数据类型,所以在声明创建模板时应指定两种数据类型,我们这里指定为 NameType和AgeType ,因此整体语句为: template<class NameType,  calss AgeType>。

   2: 在使用类模板实例化对象时,需要显示地指定数据类型(尖括号<> 就是模板的参数列表)。

代码

#include<iostream>
using namespace std;

template<class NameType,class AgeType>
class Person
{
public:
	Person(NameType name, AgeType age) {
		this->m_Name = name;
		this->m_Age = age;
	}

	void ShowPerson() {
		cout << "name: " << this->m_Name << "  age: " << this->m_Age << endl;
	}

public:
	NameType m_Name;
	AgeType m_Age;

};

void test() {
	Person<string, int> p("zhangsan", 1000);
	p.ShowPerson();
}

int main(void) {
	test();
}


// 打印结果

name: zhangsan  age: 1000

从 运行的结果知:函数模板正确实例化出对象并调用了相应的成员函数

2:类模板和函数模板区别

类模板和函数模板主要有两点区别

1:类模板没有自动类型推导的使用方式

在案例中:在上面Person类模板中,如果我们隐式的初始化一个对象(Person P ("zhangsan",1000)) 不指定模板参数列表会报错提示:缺少类模板的参数列表。

2:如果想要隐式的初始化对象,那么我们可以为:AgeType指定默认数据类型 int , 为 NameType指定默认数据类型 String, 但是尖括号<> 还是需要保留。

#include<iostream>
using namespace std;

template<class NameType = string,class AgeType = int>
class Person
{
public:
	Person(NameType name, AgeType age) {
		this->m_Name = name;
		this->m_Age = age;
	}

	void ShowPerson() {
		cout << "name: " << this->m_Name << "  age: " << this->m_Age << endl;
	}

public:
	NameType m_Name;
	AgeType m_Age;

};

void test() {
	Person<> p("zhangsan", 1000);
	p.ShowPerson();
}

int main(void) {
	test();
}

// 打印结果

name: zhangsan  age: 1000

 从打印结果可以看出,省略了模板参数列表中的内容,仍然可以实例化对象。

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

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

1: 普通类中的成员函数一开始就是可以创建

2:类模板中的成员函数在调用时才创建

案例:

创建两个普通类 Person1和Person2 ,他们各自有一个成员函数showPerson1和showPerson2,接着创建一个类模板 Myclas,含有一个成员变量obj,还含有两个成员函数func1和func2,分别实现调用obj的showPerson1和showPerson2成员函数。

#include<iostream>
#include<string>
using namespace std;

class Person1
{
public:
	void showPerson1()
	{
		cout << "Person1 show" << endl;
	}
};

class Person2
{
public:
	void showPerson2() {
		cout << "Person2 show" << endl;
	}

};

template<class T>
class MyClass {
public:
	T obj;
	// 类模板中的成员函数,并不是一开始就创建,而是在模板调用时再生成。
	void func1() {
		obj.showPerson1();
	}

	void func2() {
		obj.showPerson2();
	}

};

int main() {
	MyClass<Person1> m1;
	m1.func1();

	MyClass<Person2> m2;
	m2.func2();
	return 0;
}

// 打印结果
Person1 show
Person2 show

 4:模板对象做函数参数

类模板实例化出的对象,向函数传参一共有三种方式

1:指定传入的类型-----直接显示对象的数据类型

2:参数模板化 ------将对象中的参数变为模板进行传递

3:整个类模板化----将这个对象类型模板化进行传递

案例:创建一个类模板 Person ,模板参数列表指定为 T1和T2,然后实例化出对象,并通过3中不同的方式向函数传参。

#include<iostream>
#include<string>
#include<typeinfo>
using namespace std;

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

	void showPerson() {
		cout << "name = " << this->m_Name << "  age = " << this->m_Age << endl;
	}

	T1 m_Name;
	T2 m_Age;

private:

};

// 1: 指定传入的类型
void printPerson1(Person<string, int>& p) {
	p.showPerson();
}

// 2: 参数模板化(类模板+函数模板)
template<typename T1,typename T2>
void printPerson2(Person<T1, T2>& p) {
	p.showPerson();
	cout << "T1 type:" << typeid(T1).name() << endl;
	cout << "T2 type:" << typeid(T2).name() << endl;
}

// 3: 整个类模板化(类模板+函数模板)
template<class T>
void printPerson3(T& p) {
	p.showPerson();
}

int main() {
	Person<string, int> person1("wukong", 100);
	printPerson1(person1);

	Person<string, int> person2("bajie", 90);
	printPerson2(person2);

	Person<string, int> person3("shaseng", 30);
	printPerson3(person3);
	return 0;
}

// 打印
name = wukong  age = 100
name = bajie  age = 90

name = shaseng  age = 30

 5:类模板与继承

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

1:当子类继承的父类是一个类模板时,子类在声明的时候,需要指定父类中的 T类型

2:如果不指定,编译器无法给子类分配内存

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

案例

创建一个类模板 Base ,模板的数据类型是 T。然后创建另外一个类 Son1 以指定具体模板数据类型的方式去继承这个类模板,创建另外一个类 Son2 以灵活指定出父类中T 的类型的方法区继承这个类模板。

#include<iostream>
#include<string>
#include<typeinfo>
using namespace std;

template<class T>
class Base {
public:
	T m;
};

// 继承时,必须知道父类中 T的类型才可以向下继承
class Son1 :public Base<int> {
public:
	Son1() {
		cout << typeid(m).name() << endl;
	}
};

// 如果想保留父类中 T的类型,子类也需要变为类模板
// 这里子类模板没有 T类型数据
template<class T1>
class Son2 :public Base<T1> {
public:
	Son2() {
		cout << typeid(T1).name() << endl;
	}
};

// 这里子类模板具有T2数据类型,T3继承自父类模板 Base
template<class T2,class T3>
class Son3 :public Base<T3> {
public:
	Son3() {
		cout << typeid(T2).name() << "  " << typeid(T3).name() << endl;
	}
};

int main() {
	Son1 c1;
	Son2<int> c2;
	Son3<int, char> c3;
	return 0;
}

// 打印结果
int
int
int char

6 : 类模板成员函数类外实现

类模板中成员函数类外实现时,需要加上模板参数列表。

案例:创建一个类模板Person 时,在类模板内部只给出成员函数的声明,在类外实现成员函数的定义。

#include<iostream>
#include<string>
using namespace std;

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

public:
	T1 m_Name;
	T2 m_Age;
};

// 构造函数类外实现
// 类外实现的成员函数需要加上类模板参数列表
template <class T1,class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
	this->m_Name = name;
	this->m_Age = age;
}

//成员函数类外实现
template<class T1,class T2>
void Person<T1, T2> ::showPerson() {
	cout << "person name: " << this->m_Name << "  person age: " << this->m_Age << endl;
}

int main() {
	Person<string, int> p("tom", 20);
	p.showPerson();
	return 0;
}

// 打印结果
person name: tom  person age: 20

7:类模板分文件编写

类模板成员函数分文件编写产生的问题以及解决方式

问题:

类模板中成员函数创建时机是在调用阶段,这导致文件编译时链接不到

解决方案:

1:直接包含.cpp 源文件

2:  将声明和定义写在同一个文件中。

案例: 先定义普通类(非模板类):我们可以看到:程序可以正常运行(这也说明了 普通类的成员函数开始就会创建,所以编译器在链接阶段会根据Person.h 文件找到成员函数的定义)

// Person.h

#pragma once
#include<iostream>
#include<string>
using namespace std;


class  Person
{
public:
	Person(string name, int age);
	void showPerson();

	string m_Name;
	int m_Age;

};



// Person.cpp

#include "Person.h"
Person::Person(string name, int age) {
	this->m_Name = name;
	this->m_Age = age;
}

void Person::showPerson() {
	cout << "person name: " << this->m_Name << "  person age: " << this->m_Age << endl;
}


// main.cpp
#include"Person.h"
int main() {
	Person person("mali", 18);
	person.showPerson();
}

// 打印结果
person name: mali  person age: 18


在看下如果是声明的类模板函数

//  Person.h

#pragma once
#include<iostream>
#include<string>
using namespace std;

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

	T1 m_Name;
	T2 m_Age;

};


// Person.cpp
#include "Person.h"
template<class T1,class T2>
Person<T1,T2>::Person(T1 name, T2 age) {
	this->m_Name = name;
	this->m_Age = age;
}

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


// main.cpp

#include"Person.h"
int main() {
	Person<string,int> person("mali", 18);
	person.showPerson();
}

// 打印结果
1>D:\C++\ProjectCPlus\Debug\MainDemo5.exe : fatal error LNK1120: 2 个无法解析的外部命令
1>已完成生成项目“MainDemo5.vcxproj”的操作 - 失败。
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========

运行,可以看到链接过程会出错,提示无法解析这些成员函数。这是因为主函数只包含了person.h,而person.h中只有类模板成员函数的声明而没有定义。同时,类模板的成员函数一开始是不会创建的,所以主函数在包含person.h的时候,不会生成这两个成员函数。这也导致了编译器看不到person.cpp中对函数模板成员函数的定义,所以在链接阶段找不到这两个成员函数的定义
 

解决方案1: main.cpp 直接包含 .cpp 源文件 ,并且 Person.cpp 文件添加  #pragma once

// Person.cpp

#pragma once
#include "Person.h"
template<class T1,class T2>
Person<T1,T2>::Person(T1 name, T2 age) {
	this->m_Name = name;
	this->m_Age = age;
}

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



// main.cpp
#include"Person.cpp"
#include"Person.cpp"
int main() {
	Person<string,int> person("mali", 18);
	person.showPerson();
}

// 打印结果
person name: mali  person age: 18

8:类模板与友元

全局函数做有元的类内外实现

9:类模板案例:数组类封装

案例描述

实现一个通用的数组类,要求如下:

1:可以对内置数据类型以及自定义数据类型的数据进行存储

2:将数组中的数据存储到堆区

3:构造函数中可以传入素组的容量

4:提供对应的拷贝构造函数以及operator 防止浅拷贝问题

5:提供尾插法和尾删法对数组中的数据进行增加和删除

6:可以通过下标的方式访问数组中的元素

7:可以获取数组中当前元素个数和数组的容量

参考链接

C++核心编程:P13->模板----类模板_爱你哦小猪猪的博客-CSDN博客_缺少类模板的参数列表

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
写一段JQuery 实现以下逻辑 var hd =$('#txtHeadRailQty').val();// 这个是Common ValanceHeadRail Number: if(hd == 2) //HeadRail Number:2 var cm =$('#CommonValance').val(); //这个是Blind Type if(cm == 'Common Valance') //Blind Type為Common Valance var mt= $('#txtMeasuringType').val()// 这个为WS 或者MFF if(mt =='WS') 这个为WS if(mt == 'MFF') 这个为MFF var lw =$('#txtLeftWidth').val();//这个是Left Width var ltp =$('#txtLeftTiltPos').val(); 这个是Left Width 对应的wand var cw =$('#txtCenterWidth').val();这个 是Center Left Width var ltp =$('#txtCenterTiltPos').val(); 这个是Center Left Width 对应的wand var cwb =$('#txtCenterWidthB').val();//这个是Center Right Width var ltp =$('#txtCenterTiltPosB').val(); 这个是Center Right Width 对应的wand var rw =$('#txtRightWidth').val();//这个是Right Width var ltp =$('#txtRightTiltPos').val(); 这个是Right Width 对应的wand var lgw = $('#txtLeftGapWidth').val() // 这个是Left Gap Width var rgw =$('#txtRightGapWidth').val() //这个是Right Gap Width var cgw = $('#txtCenterGapWidth').val() // 这个是Center Gap Width HeadRail Number:2,WS時 Left Width在 165mm+5mm+Left Gap Width/2~380+5mm+Left Gap Width/2時,Wand值需顯示C Right Width在 165mm+5mm+Left Gap Width/2~380+5mm+Left Gap Width/2時,Wand值需顯示C HeadRail Number:3,WS時 Left Width在 165mm+5mm+Left Gap Width/2~380+5mm+Left Gap Width/2時,Wand值需顯示C Center Width 在 165mm+Left Gap Width/2+Right Gap Width/2~380+Left Gap Width/2+Right Gap Width/2 時,Wand值需顯示C Right Width在 165mm+5mm+ Right Gap Width/2~380+5mm+ Right Gap Width/2時,Wand值需顯示C HeadRail Number:4,WS時 Left Width在 165mm+5mm+Left Gap Width/2~380+5mm+Left Gap Width/2時,Wand值需顯示C Center Left Width 在 165mm+Left Gap Width/2+ Center Gap Width/2~380+Left Gap Width/2+ Center Gap Width/2時,Wand值需顯示C Center Right Width 在 165mm+ Center Gap Width/2+ Right Gap Width/2~380+ Center Gap Width/2+ Right Gap Width/2時,Wand值需顯示C Right Width在 165mm+5mm+ Right Gap Width/2~380+5mm+ Right Gap Width/2時,Wand值需顯示C HeadRail Number:2,MFF時 Left Width在 165mm+Left Gap Width/2~380+Left Gap Width/2時,Wand值需顯示C Right Width在 165mm+Left Gap Width/2~380+Left Gap Width/2時,Wand值需顯示C HeadRail Number:3,MFF時 Left Width在 165mm+Left Gap Width/2~380+Left Gap Width/2時,Wand值需顯示C Center Width 在 165mm+Left Gap Width/2+Right Gap Width/2~380+Left Gap Width/2+Right Gap Width/2 時,Wand值需顯示C Right Width在 165mm+ Right Gap Width/2~380+ Right Gap Width/2時,Wand值需顯示C HeadRail Number:4,MFF時 Left Width在 165mm+Left Gap Width/2~380+Left Gap Width/2時,Wand值需顯示C Center Left Width 在 165mm+Left Gap Width/2+ Center Gap Width/2~380+Left Gap Width/2+ Center Gap Width/2時,Wand值需顯示C Center Right Width 在 165mm+ Center Gap Width/2+ Right Gap Width/2~380+ Center Gap Width/2+ Right Gap Width/2時,Wand值需顯示C Right Width在 165mm+ Right Gap Width/2~380+ Right Gap Width/2時,Wand值需顯示C
06-13

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值