[C++ 改进编程] (2):模板 - 类模板
最编程
2024-03-20 14:54:25
...
目录
类模板语法
类模板与函数模板的区别
类模板中的成员函数创建时机
类模板对象做函数参数
类模板与继承
类模板成员函数类外实现
类模板份文件编写
类模板与友元
类模板使用示例
类模板的作用
建立一个通用类,类中的成员,数据类型可以不具*定,用一个虚拟类型来代表
类模板语法
template<typename T>
类
类模板与函数模板的区别
- 类模板没有自动类型推导的使用方式
- 类模板在模板参数列表中可以有默认参数
类模板中的成员函数创建时机
- 普通类中的成员函数一开始声明就可以创建
- 类模板中的成员函数在调用时才创建
类模板对象做函数参数
-
指定传入的类型-----直接显示对象的数据类型(广泛使用)
-
参数模板化-----将对象中的参数变为模板进行传递
-
整个类模板化-----将这个对象类型模板化进行传递
类模板与继承
- 当子类继承的父类是一个类模板时,子类在声明的时候要指定出父类中T的类型
- 如果不指定,编译器无法给子类分配内存
- 如果想灵活指定出父类中T的类型,子类也需要变为类模板
#include <iostream>
#include <string>
using namespace std;
template <class T>
class Base
{
public:
T m;
};
//继承,父类为类模板, 1.指定类型
class Son1 :public Base<int>
{
};
//继承,父类为类模板, 2.灵活继承
template<class S1, class NameType>
class Son2 :public Base<NameType>
{
public:
S1 m_obj;
Son2(S1 obj, NameType name)
{
this->m_obj = obj;
this->m = name;
}
void show_info()
{
cout << "son m_obj:" << this->m_obj << endl;
cout << "son m:" << this->m << endl;
}
};
void test_Son(void)
{
Son2<string, int> m_son("son", 12);
m_son.show_info();
}
int main()
{
test_Son();
return 0;
}
类模板成员函数类外实现
类模板中成员函数类外实现时,需要加上模板参数列表
#include <iostream>
#include <string>
using namespace std;
template<class NameType, class AgeType> //默认参数
class Person
{
public:
Person(NameType name, AgeType age);
void show_Info();
NameType m_Name;
AgeType m_Age;
};
//类外实现构造函数
template<class NameType, class AgeType>
Person<NameType, AgeType>::Person(NameType name, AgeType age)
{
this->m_Name = name;
this->m_Age = age;
}
//类外实现成员函数
template<class NameType, class AgeType>
void Person<NameType, AgeType>::show_Info()
{
cout << "姓名:" << this->m_Name << ",年龄:" << this->m_Age << endl;
}
void test(void)
{
Person<string, int> person("张三", 19); //必须显示指定类型
person.show_Info();
}
int main()
{
test();
return 0;
}
类模板份文件编写
问题:类模板中成员函数创建时机在调试阶段,导致份文件编写时链接不到。
解决方案:
- 1.直接包含.cpp源文件
//修改头文件#include "person.h"改为
#include "person.hpp"
- 2.将声明和实现写到同一个文件中,并修改后缀名为.hpp, hpp是约定名称,非强制。
//声明和实现写一起命名为.hpp文件,然后包含hpp文件
#include "person.hpp"
类模板与友元
- 全局函数类内实现---直接在类内声明友元即可(建议使用)
- 全局函数类外实现---需要提前让编译器知道全局函数的存在
#include <iostream>
#include <string>
using namespace std;
//提前让编译器知道Person存在
template<class NameType, class AgeType> class Person;
//全局函数类外实现
template<class NameType, class AgeType>
void show_Info1(Person<NameType, AgeType>p)
{
cout << "姓名:" << p.m_Name << ",年龄:" << p.m_Age << endl;
}
template<class NameType, class AgeType> //默认参数
class Person
{
public:
Person(NameType name, AgeType age);
//全局函数配合友元类内实现
friend void show_Info(Person<NameType, AgeType>p)
{
cout << "姓名:" << p.m_Name << ",年龄:" << p.m_Age << endl;
}
//全局函数类外实现 ;加空模板参数列表 ;编译器提前知道这个函数的存在
friend void show_Info1<>(Person<NameType, AgeType>p);
private:
NameType m_Name;
AgeType m_Age;
};
template<class NameType, class AgeType>
Person<NameType, AgeType>::Person(NameType name, AgeType age)
{
this->m_Name = name;
this->m_Age = age;
}
void test(void)
{
Person<string, int> person("张三", 19); //必须显示指定类型
show_Info(person);
show_Info1(person);
}
int main()
{
test();
return 0;
}
类模板使用示例
#include <iostream>
#include <string>
using namespace std;
template<class NameType, class AgeType = int> //默认参数
class Person
{
public:
Person(NameType name, AgeType age)
{
this->m_Name = name;
this->m_Age = age;
}
void show_Info()
{
cout << "姓名:" << this->m_Name << ",年龄:" << this->m_Age << endl;
}
NameType m_Name;
AgeType m_Age;
};
void test(void)
{
Person<string, int> person("张三", 19); //必须显示指定类型
Person<string> person_1("李四", 19);
person.show_Info();
person_1.show_Info();
}
//指定传入类型
void printPerson(Person<string, int> &person)
{
person.show_Info();
}
//将参数模板化
template<class NameType, class AgeType>
void printPerson1(Person<NameType, AgeType>& person)
{
person.show_Info();
cout << "NameType数据类型为:" << typeid(NameType).name() << endl;
cout << "AgeType数据类型为:" << typeid(AgeType).name() << endl;
}
//整个类模板化
template<class T>
void printPerson1(T & person)
{
person.show_Info();
cout << "T数据类型为:" << typeid(T).name() << endl;
}
void test_parm(void)
{
//指定传入类型
Person<string, int> person("张三", 199);
printPerson(person);
//将参数模板化
printPerson1(person);
//整个类模板化
printPerson1(person);
}
int main()
{
test();
test_parm();
return 0;
}
推荐文章:[C++提高编程](一):模板----函数模板
推荐阅读
-
C++ 语法错误:类模板成员函数不能是虚函数,怎么办?
-
[C++ 改进编程] (2):模板 - 类模板
-
C++ 类模板手写向量容器,实战详细版本
-
全面掌握C++核心语法:面向对象的友元、内部类和局部类,以及强化训练(数组类封装)等知识点。此外,你还将学习运算符重载、仿函数、模板、类型转换、C++标准、错误处理和异常、以及智能指针等内容。
-
计算机视觉中,究竟有哪些好用的目标跟踪算法(下)-快速变形主要因为CF是模板类方法。容易跟丢这个比较好理解,前面分析了相关滤波是模板类方法,如果目标快速变形,那基于HOG的梯度模板肯定就跟不上了,如果快速变色,那基于CN的颜色模板肯定也就跟不上了。这个还和模型更新策略与更新速度有关,固定学习率的线性加权更新,如果学习率太大,部分或短暂遮挡和任何检测不准确,模型就会学习到背景信息,积累到一定程度模型跟着背景私奔了,一去不复返。如果学习率太小,目标已经变形了而模板还是那个模板,就会变得不认识目标。(举个例子,多年不见的同学,你很可能就认不出了,而经常见面的同学,即使变化很大你也认识,因为常见的同学在你大脑里面的模型在持续更新,而多年不见就是很久不更新) 快速运动主要是边界效应(Boundary Effets),而且边界效应产生的错误样本会造成分类器判别力不够强,下面分训练阶段和检测阶段分别讨论。 训练阶段,合成样本降低了判别能力。如果不加余弦窗,那么移位样本是长这样的: 除了那个最原始样本,其他样本都是“合成”的,100*100的图像块,只有1/10000的样本是真实的,这样的样本集根本不能拿来训练。如果加了余弦窗,由于图像边缘像素值都是0,循环移位过程中只要目标保持完整那这个样本就是合理的,只有目标中心接近边缘时,目标跨越边界的那些样本是错误的,这样虽不真实但合理的样本数量增加到了大约2/3(padding= 1),即使这样仍然有1/3(3000/10000)的样本是不合理的,这些样本会降低分类器的判别能力。再者,加余弦窗也不是“免费的”,余弦窗将图像块的边缘区域像素全部变成0,大量过滤掉分类器本来非常需要学习的背景信息,原本训练时判别器能看到的背景信息就非常有限,我们还加了个余弦窗挡住了背景,这样进一步降低了分类器的判别力(是不是上帝在我前遮住了帘。不是上帝,是余弦窗)。 检测阶段,相关滤波对快速运动的目标检测比较乏力。相关滤波训练的图像块和检测的图像块大小必须是一样的,这就是说你训练了一个100*100的滤波器,那你也只能检测100*100的区域,如果打算通过加更大的padding来扩展检测区域,那样除了扩展了复杂度,并不会有什么好处。目标运动可能是目标自身移动,或摄像机移动,按照目标在检测区域的位置分四种情况来看: 如果目标在中心附近,检测准确且成功。 如果目标移动到了边界附近但还没有出边界,加了余弦窗以后,部分目标像素会被过滤掉,这时候就没法保证这里的响应是全局最大的,而且,这时候的检测样本和训练过程中的那些不合理样本很像,所以很可能会失败。 如果目标的一部分已经移出了这个区域,而我们还要加余弦窗,很可能就过滤掉了仅存的目标像素,检测失败。 如果整个目标已经位移出了这个区域,那肯定就检测失败了。 以上就是边界效应(Boundary Effets),推荐两个主流的解决边界效应的方法,但速度比较慢,并不推荐用于实时场合。