IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> c++ advanced(7)best example to understand CRTP -> 正文阅读

[数据结构与算法]c++ advanced(7)best example to understand CRTP

CRTP

全称是curiously recurring template pattern。

在搞明白什么已经为什么需要CRTP之前,我们先假定一个场景: 我们想要用很多基于某个基类的衍生类,存入一个容器中,然后让这些衍生类做一个深拷贝,存入另一个容器中。这是比较常见的一种实际开发场景。

比如这种:

struct shape 
{
	virtual ~shape() = default;
};
struct circle:public shape
{
	int radius = 1;
};
struct rectangle:public shape
{
	int width = 1;
	int length = 2;
};

我们在函数中可能会这样写:

int main()
{
	vector<unique_ptr<shape>> source;
	vector<unique_ptr<shape>> target;
	source.emplace_back(make_unique<circle>());
	source.emplace_back(make_unique<rectangle>());
	for (auto&& p : source)
	{
		target.emplace_back(make_unique<shape>(*p));
	}
	return 0;
}

事实上,运行能通过,但是,如果我们改成这样:

using namespace std;
struct shape 
{
	virtual void message()
	{
		cout << "this is the base struct" << endl;
	};
	virtual ~shape() = default;
};
struct circle:public shape
{
	void message() final 
	{
		cout << "this is a circle" << endl;
	}
	circle(int i) :radius(i) {};
	int radius ;
};
struct rectangle:public shape
{
	void message() final
	{
		cout << "this is a rectangle" << endl;
	}
	rectangle(int w, int l) :width(w), length(l) 
	{};
	int width = 1;
	int length = 2;
};
int main()
{
	shape* s=new rectangle(1, 2);
	vector<unique_ptr<shape>> source;
	vector<unique_ptr<shape>> target;
	source.emplace_back(make_unique<circle>(1));
	source.emplace_back(make_unique<rectangle>(1,2));
	for (auto&& p : source)
	{
		p->message();
		target.emplace_back(make_unique<shape>(*p)); 
	}
	for (auto&& p : target) 
	{
		p->message();
	}
	return 0;
}

结果是:

this is a circle
this is a rectangle
this is the base struct
this is the base struct

然后我们发现其实子类根本没有实现对应的拷贝构造,因为我们调用make_unique<shape>实际上是在构造一个shape的类型,它本身并不会根据我们传入的类型不同而选择合适的派生类。最简单的解决方法如下:

using namespace std;
struct shape 
{
	virtual shape* clone() = 0;
	virtual void message()
	{
		cout << "this is the base struct" << endl;
	};
	virtual ~shape() = default;
};
struct circle:public shape
{
	shape* clone() final 
	{
		return new circle(*this);
	}
	void message() final 
	{
		cout << "this is a circle" << endl;
	}
	circle(int i) :radius(i) {};
	int radius ;
};
struct rectangle:public shape
{
	shape* clone() final
	{
		return new rectangle(*this);
	}
	void message() final
	{
		cout << "this is a rectangle" << endl;
	}
	rectangle(int w, int l) :width(w), length(l) 
	{};
	int width = 1;
	int length = 2;
};
int main()
{
	
	vector<unique_ptr<shape>> source;
	vector<unique_ptr<shape>> target;
	source.emplace_back(make_unique<circle>(1));
	source.emplace_back(make_unique<rectangle>(1,2));
	for (auto&& p : source)
	{
		p->message();
		target.emplace_back(unique_ptr<shape>(p->clone())); 
	}
	for (auto&& p : target) 
	{
		p->message();
	}
	return 0;
}

输出的结果为:

this is a circle
this is a rectangle
this is a circle
this is a rectangle

?然后如果这样的类型成千上万,明显不能够手撸,我们学过泛型编程,那么很容易想到这个方法:

struct shape 
{
	template<typename T>
	T* clone() 
	{
		return new T(*this);
	};
	virtual void message()
	{
		cout << "this is the base struct" << endl;
	};
	virtual ~shape() = default;
};

那么问题来了,在emplace_back的时候不知道选择什么类型:

int main()
{
	
	vector<unique_ptr<shape>> source;
	vector<unique_ptr<shape>> target;
	source.emplace_back(make_unique<circle>(1));
	source.emplace_back(make_unique<rectangle>(1,2));
	for (auto&& p : source)
	{
		p->message();
		target.emplace_back(unique_ptr<shape>(p->clone<>())); //???
	}
	for (auto&& p : target) 
	{
		p->message();
	}
	return 0;
}

那么好,有人说可以选择shape,我们试试:

target.emplace_back(unique_ptr<shape>(p->clone<shape>())); //shape

?输出如下,如我们所见,这根本行不通,跟我们最开始错误一样,这种克隆方式会导致信息的丢失。

this is a circle
this is a rectangle
this is the base struct
this is the base struct

?所以我们需要对T的做进一步的限制,也就是在传入的时候让它自动推断我们的T是什么,而不是用手撸的方式。

CRTP实际上就解决了这个问题,一起来学习一下:

using namespace std;
struct shape 
{
	virtual shape* clone() = 0;
	virtual void message()
	{
		cout << "this is the base struct" << endl;
	};
	virtual ~shape() = default;
};
template<typename T>
struct shapeCRTP:public shape
{
	shape* clone() final 
	{
		return new T(*static_cast<T*>(this));
	};
};
struct circle:public shapeCRTP<circle>
{
	void message() final
	{
		cout << "this is a circle" << endl;
	}
	circle(int i) :radius(i) {};
	int radius ;
};
struct rectangle :public shapeCRTP<rectangle>
{
	void message() final
	{
		cout << "this is a rectangle" << endl;
	}
	rectangle(int w, int l) :width(w), length(l) 
	{};
	int width = 1;
	int length = 2;
};

有几点要注意:

  1. 中间多了一个shapeCRTP类,派生类继承的是它而不是shape,
  2. 注意T(*static_cast<T*>(this));,需要转换为T类型,因为this本身是shapeCRTP,我们希望生成的子类是circle或者rectangle,shapeCRTP的作用仅仅是帮助我们针对clone这个函数自动绑定子类的类型信息。
  3. 在完成后不需要再指定clone的类型,因为会自动调用shapeCRTP中的clone函数,而T根据circle以及rectangle的不同已经分别指定了其类型
int main()
{
	
	vector<unique_ptr<shape>> source;
	vector<unique_ptr<shape>> target;
	source.emplace_back(make_unique<circle>(1));
	source.emplace_back(make_unique<rectangle>(1,2));
	for (auto&& p : source)
	{
		p->message();
		target.emplace_back(unique_ptr<shape>(p->clone())); //???
	}
	for (auto&& p : target) 
	{
		p->message();
	}
	return 0;
}

输出的结果为:

this is a circle
this is a rectangle
this is a circle
this is a rectangle

行,摸了

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-04-04 12:36:03  更:2022-04-04 12:37:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 9:49:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码