C++强化突破(1)

文章介绍了如何在C++中自定义Point结构体,并将其作为unordered_map的键。作者详细展示了如何定义结构体及其成员函数,以及在使用unordered_map过程中遇到的hash_function未初始化问题,最后通过将hash_function封装到类中解决了这个问题。
摘要由CSDN通过智能技术生成

自定义结构体,并作为map/set/unordered_map/unordered_set的key值

一、代码背景:

  • 为了更好的开发空间图形方面的程序,因此需要自定义个图形化常用的单位——坐标点。传统的方位点一般通过二维或者三维数组来展示和计算,这让一些缺乏空间维度思维的程序员来说,加重了对内部数据过多关注而带来的开销。因此我们通过定义点对象,来封装空间的数据,这样,程序员能更直观的理解和感受空间维度和各种几何计算。
  • 因此,该篇文章抛砖引玉,先搭基建,然后在通过这个基建,来扩展空间几何的应用开发。

二、定义Point结构体:

  • 首先需要一个点的结构体或者类(该篇暂定结构体);而结构体中需要有一下基本函数和变量;
// point.h

#include <string>

using namespace std;

struct Point
{
	// 默认构造函数
	Point() : x(0), y(0), z(0){}
	Point(double x, double y, double z) : x(x), y(y), z(z) { is_valid = true; }		// 此处设置为了更好的判断点的有效性
	
	// 拷贝构造函数
	Point(const Point& other)
	{
		x = other.x;
		y = other.y;
		z = other.z;
		is_valid = other.is_valid;
	}

	// 赋值函数
	Point&& operator= (const Point& other)
	{
		Point pt;
		pt.x = other.x;
		pt.y = other.y;
		pt.z = other.z;
		is_valid = other.is_valid;
		return move(pt);
	}

	// 重载操作符+
	Point&& operator+(const Point& other)
	{
		Point pt;
		pt.x = this->x + other.x;
		pt.y = this->y + other.y;
		pt.z = this->z + other.z;
		return move(pt);
	}

	// 重载操作符-
	Point&& operator-(const Point& other)
	{
		Point pt;
		pt.x = this->x - other.x;
		pt.y = this->y - other.y;
		pt.z = this->z - other.z;
		return move(pt);
	}
	
	// 重载操作符<  用于后续的比较
	bool operator<(const Point& other)
	{
		if (x == other.x)
		{
			if (y == other.y) return z < other.z;
			else return y < other.y;
		}
		else return x < other.x;
	}
	// 此处为了后续更好的打印展示出来,因此将坐标点转化为字符串
	string GetPointByStr() 
	{
		return  ("(" + to_string(x) + ", " + to_string(y) + ", " + to_string(z) + ")");
	}

	double x;
	double y;
	double z;
	bool is_valid = false;
};

三、使用unordered_map进行装载Point和每个点所对应的信息,如该点有多少平方(double),有几棵树(int),是什么位置(string)……

此处暂时表示一个点所占区域大小,即<Point, double>

// main.cpp
#include <unordered_map> 
#include <iostream>
#include <string>

#include "point.h"

using namespace std;

int main() 
{
	Point a = Point(1, 2, 3);
	Point b = Point(2, 32, 13);
	Point c = Point(-3, 43, 22);

	// 校验重载操作符< 的实现情况
	if (a < b)
	{
		cout << "a < b" << endl;
	}
	else
	{
		cout << "a > b" << endl;
	}

	
	unordered_map<Point, double> map;

	map.insert(pair<Point, double>(a, 12.34));
	map.emplace(pair<Point, double>(b, 9.01));
	map.emplace(pair<Point, double>(c, 1.94));

	for (auto iter : map)
	{
		Point pt = iter.first;
		cout << "The loacl : " << pt.GetPointByStr() << ", and its area has " << iter.second << "平方米." << endl;
	}
}

四、结果

在这里插入图片描述
显而易见的失败,而且是生成时的问题。当然这句话的意思大概就是,你的hash_compare函数不可用,更直观的意思就是,你这个key值是你自定义的对象,c++函数库找不到该对象的hash_function,因此需要你自己去给你自定义的对象,构造一个hash_function。

因此知道问题所在的我们,又感觉行了,直接网上找一堆方案就开干;

the first one:

既然没有这个function,那么我们给它造个

// 定义Point的hash表, 并且把他所有坐标拼接在一起作为他的hash串
static size_t PointHash(const Point& self) noexcept
{
	static 	std::hash<std::string> hash_str;
	return hash_str(to_string(self.x) + to_string(self.y) + to_string(self.z));
}

//  并且在Point中增加 一些compare有关的函数
stuct Point
{
	...... // 其他函数与上面一致
	// 注意  这个const 必须要加入,不然会导致operator()实现不了
	// 养成好习惯,不改变数据的函数体 后面加个const来确保不会对数据更改
	bool operator==(const Point& other) const
	{
		return (x == other.x) && (y == other.y) && (z == other.z);
	}

	bool operator()(const Point& a, const Point& b) const
	{
		return a == b;
	}
	...... // 变量与上面一致
}

然后主函数仅修改unordered_map的声明;

....... // 头文件信息与上 一致
int main() 
{
	Point a = Point(1, 2, 3);
	Point b = Point(2, 32, 13);
	Point c = Point(-3, 43, 22);

	if (a < b)
	{
		cout << "a < b" << endl;
	}
	else
	{
		cout << "a > b" << endl;
	}

	
	unordered_map<Point, double, decltype(&PointHash)> map;

	map.emplace(pair<Point, double>(a, 12.34));
	map.emplace(pair<Point, double>(b, 9.01));
	map.emplace(pair<Point, double>(c, 1.94));

	for (const auto& iter : map)
	{
		Point pt = iter.first;
		cout << "The loacl : " << pt.GetPointByStr() << ", and its area has " << iter.second << "平方米." << endl;
	}
}

好,完事具备,直接跑起来:
在这里插入图片描述
看起来生成没问题,然而结果又又又有问题了,请看VCR:
在这里插入图片描述
显然,这里表示非法占用空间了, 0X00000这块空间,显然是我们不能用的,因此表明我们在初始化时有对象没有申请空间,那我们就会想了,我们又没有用到指针,就只是一个结构体,按理来说系统会自动申请和分配的,那到底哪里出问题了呢?
点开下面的debug数据,看到map中hash_function居然是 0X00000,。。。垂死病中惊坐起,小丑竟是我自己;显然这个hash_function没有分配空间,也就是未初始化。
然后找到问题所在,那我们重新找个方案——将我们的hash_function封装在一个类中

class PointHasher {//hash函数,得到hash码
public:
	size_t operator()(const Point& pt)const {
		return hash<string>()(to_string(pt.x) + to_string(pt.y) + to_string(pt.z));
	}
};

主函数:

int main() 
{
	Point a = Point(1, 2, 3);
	Point b = Point(2, 32, 13);
	Point c = Point(-3, 43, 22);
	
	//unordered_map<Point, double, decltype(&PointHash)> map;
	unordered_map<Point, double, PointHasher> map;

	map.emplace(pair<Point, double>(a, 12.34));
	map.emplace(pair<Point, double>(b, 9.01));
	map.emplace(pair<Point, double>(c, 1.94));

	for (const auto& iter : map)
	{
		Point pt = iter.first;
		cout << "The loacl : " << pt.GetPointByStr() << ", and its area has " << iter.second << "平方米." << endl;
	}
}

最终,执行成功,我们可以看到,map的数据就变成正常的了
在这里插入图片描述
控制台:
在这里插入图片描述

五、总结

  • 单独自定义一个对象或者结构体很简单,但是实现它的强扩展性,还需要更进一步的研习c++标准库源码,重载各种各样的函数和组件。
  • 遗留一些问题,后续更深入了解后,再做补充——如
    • 1、为什么声明一个静态hash_function,装载到unordered_map中,会造成未初始化的问题,而封装到一个类中就没有这样的问题呢?
    • 2、是类和静态函数的底层问题导致,还是hash底层问题导致的?
  • 也欢迎各位大佬指点12。

TOBECONTINUE。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值