目录
总体实现代码
题目
定义一个集合类setColour,要求元素为枚举类型值。例如,
enum colour { red, yellow, blue, white, black };
集合类实现交、并、差、属于、蕴含、输入、输出等各种基本运算。设计main函数测试setColour类的功能。
前言
集合的实现的两种方法:
1.用bool数组每个数组元素存储一种集合元素
2.用一个unsigned int存储,每一个二进制位代表一个集合元素
从实现上,方法一更好理解,但无论是从时间角度、空间角度还是代码量(行数足足有二倍之差)方法二都有明显优势。
下文将分别对两种方法进行讲解,除集合的实现原理不同,在方式二的实现过程中,还进行了实现方式的优化和代码拓展性的延申,可以在宏变量增加元素。
枚举类型:
在枚举类型中,共五种颜色,若不进行初始化,其默认从0开始向后累加1,如下测试:
方式一
总体实现原理
1.我们定义一个bool型数组作为成员变量,长度为4,每个位置作为以一种颜色,此颜色在集合中,就置1,否则为0,如下:
2.接下来,各种集合运算就可以通过遍历数组,对每一个元素进行逻辑运算操作而实现。
各函数实现
class setColour
{
public:
setColour();//构造函数
setColour(const setColour& c);//拷贝构造函数
setColour& operator=(const setColour& c);//赋值运算符重载
setColour operator+(setColour& c2);//并集
setColour operator*(setColour& c2);//交集
setColour operator-(setColour& c2);//差集
friend bool operator<(colour c, setColour& c2);//属于
bool operator<=(setColour& c2);//包含于
friend ostream& operator<<(ostream& output, const setColour& c);//流插入运算符重载
friend istream& operator>>(istream& input, setColour& c);//流提取运算符重载
private:
bool* _colourStatus;
};
为使集合的运算更直观,我们对“ * + - <= < ”等符号进行重载,用来代表交、并、差、蕴含、属于等集合运算。
* | + | - | <= | < |
交集(两个集合同时存在的元素集合) | 并集(两个集合的所有元素集合) | 差集(前一个集合有后一个集合没有的元素集合) | 蕴含(右蕴含左) | 属于(左侧元素属于右侧集合) |
构造函数
构造数组,并将每个元素初始化为0:
setColour::setColour()
{
_colourStatus = new bool[5];
for (int i = 0; i < 5; i++)
{
_colourStatus[i] = 0;
}
}
拷贝构造函数和赋值运算符重载
由于我们的成员变量是动态开辟的,但编译器给的默认拷贝构造函数和赋值重载只会进行浅拷贝,在对象析构的时候,会出现两个对象delete同一块内存的情况,而引发报错,所以我们要自己写两个深拷贝的,保证复制出的数组和原来的数组不是同一块空间:
setColour::setColour(const setColour& c)
{
_colourStatus = new bool[5];
for (int i = 0; i < 5; i++)
{
_colourStatus[i] = c._colourStatus[i];
}
}
setColour& setColour::operator=(const setColour& c)
{
if (this != &c)
{
_colourStatus = new bool[5];
for (int i = 0; i < 5; i++)
{
_colourStatus[i] = c._colourStatus[i];
}
return *this;
}
}
“>>”的 重载
在输入时,我们会输入一个字符串来表示这个集合,或添加某个元素,所以我们要用到strstr()这个函数进行颜色元素的检索,再进行数组写入:
istream& operator>>(istream& input, setColour& c)
{
char inColour[30] = { 0 };
cin.getline(inColour, 30);
if (strstr(inColour, "red"))
{
c._colourStatus[(int)red] = 1;
}
if (strstr(inColour, "yellow"))
{
c._colourStatus[(int)yellow] = 1;
}
if (strstr(inColour, "blue"))
{
c._colourStatus[(int)blue] = 1;
}
if (strstr(inColour, "white"))
{
c._colourStatus[(int)white] = 1;
}
if (strstr(inColour, "black"))
{
c._colourStatus[(int)black] = 1;
}
return input;
}
“<<”的重载
循环数组每一个元素,若此元素为1,则对这个下标表示的颜色进行输出(counter是用于格式控制的)
ostream& operator<<(ostream& output, const setColour& c)
{
cout << "{ ";
int counter = 0;
for (int i = 0; i < 5; i++)
{
if (c._colourStatus[i])
{
switch (i)
{
case 0:
cout << "red, ";
break;
case 1:
cout << "yellow, ";
break;
case 2:
cout << "blue, ";
break;
case 3:
cout << "white, ";
break;
case 4:
cout << "black, ";
break;
default:
break;
}
counter++;
}
}
if (counter > 0)
{
cout << "\b\b }";
}
else
{
cout << "\b}";
}
return output;
}
集合运算符重载
实现大致相同,都是遍历每一个元素进行逻辑运算:
setColour setColour::operator*(setColour& c2)
{
setColour newSet;
for (int i = 0; i < 5; i++)
{
newSet._colourStatus[i] = _colourStatus[i] && c2._colourStatus[i];
}
return newSet;
}
setColour setColour::operator+(setColour& c2)
{
setColour newSet;
for (int i = 0; i < 5; i++)
{
newSet._colourStatus[i] = _colourStatus[i] || c2._colourStatus[i];
}
return newSet;
}
setColour setColour::operator-(setColour& c2)
{
setColour newSet;
for (int i = 0; i < 5; i++)
{
newSet._colourStatus[i] = _colourStatus[i] && !c2._colourStatus[i];
}
return newSet;
}
bool operator<(colour c, setColour& c2)
{
if (c2._colourStatus[(int)c])
return 1;
else
return 0;
}
bool setColour::operator<=(setColour& c2)
{
for (int i = 0; i < 5; i++)
{
if (_colourStatus[i] && !c2._colourStatus[i])
{
return 0;
}
}
return 1;
}
总体实现代码
#include <assert.h>
#include <string.h>
#include <iostream>
using namespace std;
enum colour { red, yellow, blue, white, black };
class setColour
{
public:
setColour();
setColour(const setColour& c);
setColour& operator=(const setColour& c);//赋值
setColour operator+(setColour& c2);//并集
setColour operator*(setColour& c2);//交集
setColour operator-(setColour& c2);//差集
friend bool operator<(colour c, setColour& c2);//属于
bool operator<=(setColour& c2);//包含于
friend ostream& operator<<(ostream& output, const setColour& c);
friend istream& operator>>(istream& input, setColour& c);
private:
bool* _colourStatus;
};
setColour::setColour()
{
_colourStatus = new bool[5];
for (int i = 0; i < 5; i++)
{
_colourStatus[i] = 0;
}
}
setColour::setColour(const setColour& c)
{
_colourStatus = new bool[5];
for (int i = 0; i < 5; i++)
{
_colourStatus[i] = c._colourStatus[i];
}
}
setColour& setColour::operator=(const setColour& c)
{
if (this != &c)
{
_colourStatus = new bool[5];
for (int i = 0; i < 5; i++)
{
_colourStatus[i] = c._colourStatus[i];
}
return *this;
}
}
setColour setColour::operator*(setColour& c2)
{
setColour newSet;
for (int i = 0; i < 5; i++)
{
newSet._colourStatus[i] = _colourStatus[i] && c2._colourStatus[i];
}
return newSet;
}
setColour setColour::operator+(setColour& c2)
{
setColour newSet;
for (int i = 0; i < 5; i++)
{
newSet._colourStatus[i] = _colourStatus[i] || c2._colourStatus[i];
}
return newSet;
}
setColour setColour::operator-(setColour& c2)
{
setColour newSet;
for (int i = 0; i < 5; i++)
{
newSet._colourStatus[i] = _colourStatus[i] && !c2._colourStatus[i];
}
return newSet;
}
bool operator<(colour c, setColour& c2)
{
if (c2._colourStatus[(int)c])
return 1;
else
return 0;
}
bool setColour::operator<=(setColour& c2)
{
for (int i = 0; i < 5; i++)
{
if (_colourStatus[i] && !c2._colourStatus[i])
{
return 0;
}
}
return 1;
}
ostream& operator<<(ostream& output, const setColour& c)
{
cout << "{ ";
int counter = 0;
for (int i = 0; i < 5; i++)
{
if (c._colourStatus[i])
{
switch (i)
{
case 0:
cout << "red, ";
break;
case 1:
cout << "yellow, ";
break;
case 2:
cout << "blue, ";
break;
case 3:
cout << "white, ";
break;
case 4:
cout << "black, ";
break;
default:
break;
}
counter++;
}
}
if (counter > 0)
{
cout << "\b\b }";
}
else
{
cout << "\b}";
}
return output;
}
istream& operator>>(istream& input, setColour& c)
{
char inColour[30] = { 0 };
cin.getline(inColour, 30);
if (strstr(inColour, "red"))
{
c._colourStatus[(int)red] = 1;
}
if (strstr(inColour, "yellow"))
{
c._colourStatus[(int)yellow] = 1;
}
if (strstr(inColour, "blue"))
{
c._colourStatus[(int)blue] = 1;
}
if (strstr(inColour, "white"))
{
c._colourStatus[(int)white] = 1;
}
if (strstr(inColour, "black"))
{
c._colourStatus[(int)black] = 1;
}
return input;
}
方式二
总体实现原理
1.这里我们只定义了一个unsigned int(这里size_t就是unsigned int)类型,用它的32个二进制位进行集合元素的存储。(此时这种方式的劣势也显现出来,定义的集合只能有32种元素,其实也可以申请一块连续空间(数组)拓展元素个数,实现了“站着挣钱”,但后续将不能直接对一个int直接进行位运算,还需进行额外实现,各位大神如果想进行实现或有任何见解欢迎评论区一起探讨)。
2.接下类的集合运算就可以通过对这个整形进行位运算而实现。
各函数实现
1. 前面的两个宏用来进行元素扩充,COLOUR_NUM用来更改元素个数,COLOUR_STR是元素对应的字符串,在后面cin/cout会用到,再对枚举类型进行元素添加即可实现集合元素扩充
2.由于没有动态开辟空间,这里将不再需要自己写拷贝构造,赋值重载。
“>>”的 重载
1.在输入时,我们会输入一个字符串来表示这个集合,或添加某个元素,所以我们要用到strstr()这个函数进行颜色元素的检索,再进行数组写入:
2.很明显,相较方法一,这里的输入重载明显变短,那么是怎么做到的的呢?
我们定义一个字符串数组,用输入的字符串循环对这个数组进行匹配判断,从而对集合进行写入
3.如果匹配成功,这时的“ i ”就代表字符串数组的元素下标,同时也是这个颜色对应的enum赋值,也就是i代表了对应的颜色,这时我们将数字1进行左移 i 位,使它只有第 i 个二进制位是1,其他位都是0,再将这个数与原集合数字进行按位或,就可以将这一位赋值位真。
istream& operator>>(istream& input, setColour& c)
{
const char* colourIn[COLOUR_NUM] = { COLOUR_STR };
char inColour[30] = { 0 };
cin.getline(inColour, 30);
for (int i = 0; i < COLOUR_NUM; i++)
{
if (strstr(inColour, colourIn[i]))
{
c._colourStatus |= (1 << i);
}
}
return input;
}
“<<”的重载
输出函数原理大致与输入函数相同,只不过这次是循环检查哪一位是1,并打印第i位对应的字符串。
ostream& operator<<(ostream& output, const setColour& c)
{
const char* colourOut[COLOUR_NUM] = { COLOUR_STR };
cout << "{ ";
int counter = 0;
size_t colour_test = 1;
for (int i = 0; i < COLOUR_NUM; i++)
{
if (c._colourStatus & colour_test)
{
cout << colourOut[i] << ", ";
counter++;
}
colour_test <<= 1;
}
if (counter > 0)
{
cout << "\b\b }";
}
else
{
cout << "\b}";
}
return output;
}
集合运算符重载
实现大致相同,都是对元素进行位运算。
其他都好理解,这里差集要进行着重解释一下,差集就是被减集合减去交集,即:a & ~(a & b),但为什么写了a & ~b呢?这里就需要大家有一点位运算或者离散数学的知识了,运算过程如下:
a & ~(a & b) <==> a&(~a | ~b) <==> (a & ~a) | (a & ~b) <==> 1 | (a & ~b) <==> a & ~b
setColour setColour::operator*(setColour& c2)
{
return setColour(_colourStatus & c2._colourStatus);
}
setColour setColour::operator+(setColour& c2)
{
return setColour(_colourStatus | c2._colourStatus);
}
setColour setColour::operator-(setColour& c2)
{
return setColour(_colourStatus & ~c2._colourStatus);
}
bool operator<(colour c, setColour& c2)
{
return c2._colourStatus & (1 << c);
}
bool setColour::setColour::operator<=(setColour& c2)
{
return (_colourStatus | c2._colourStatus) == c2._colourStatus;
}
总体实现代码
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <errno.h>
#include <iostream>
using namespace std;
#define COLOUR_NUM 5
#define COLOUR_STR "red","yellow","blue","white","black"
enum colour { red, yellow, blue, white, black };
class setColour
{
public:
setColour(size_t cS = 0);
setColour operator+(setColour& c2);//并集
setColour operator*(setColour& c2);//交集
setColour operator-(setColour& c2);//差集
friend bool operator<(colour c, setColour& c2);//属于
bool operator<=(setColour& c2);//包含于
friend ostream& operator<<(ostream& output, const setColour& c);
friend istream& operator>>(istream& input, setColour& c);
private:
size_t _colourStatus;
};
setColour::setColour(size_t cS)
{
_colourStatus = cS;
}
setColour setColour::operator*(setColour& c2)
{
return setColour(_colourStatus & c2._colourStatus);
}
setColour setColour::operator+(setColour& c2)
{
return setColour(_colourStatus | c2._colourStatus);
}
setColour setColour::operator-(setColour& c2)
{
return setColour(_colourStatus & ~c2._colourStatus);
}
bool operator<(colour c, setColour& c2)
{
return c2._colourStatus & (1 << c);
}
bool setColour::setColour::operator<=(setColour& c2)
{
return (_colourStatus | c2._colourStatus) == c2._colourStatus;
}
ostream& operator<<(ostream& output, const setColour& c)
{
const char* colourOut[COLOUR_NUM] = { COLOUR_STR };
cout << "{ ";
int counter = 0;
size_t colour_test = 1;
for (int i = 0; i < COLOUR_NUM; i++)
{
if (c._colourStatus & colour_test)
{
cout << colourOut[i] << ", ";
counter++;
}
colour_test <<= 1;
}
if (counter > 0)
{
cout << "\b\b }";
}
else
{
cout << "\b}";
}
return output;
}
istream& operator>>(istream& input, setColour& c)
{
const char* colourIn[COLOUR_NUM] = { COLOUR_STR };
char inColour[30] = { 0 };
cin.getline(inColour, 30);
for (int i = 0; i < COLOUR_NUM; i++)
{
if (strstr(inColour, colourIn[i]))
{
c._colourStatus |= (1 << i);
}
}
return input;
}
测试主函数
void test1()
{
setColour colour1;
setColour colour2 = colour1;
cout << "请输入两行颜色集合:(如:{red, yellow, blue})" << endl;
cin >> colour1;
cin >> colour2;
setColour colour3 = colour1 * colour2;
cout << "交集:" << colour3 << endl;
colour3 = colour1 + colour2;
cout << "并集:" << colour3 << endl;
colour3 = colour1 - colour2;
cout << "差集:" << colour3 << endl;
if (red < colour1)
{
cout << "red属于第一个集合" << endl;
}
else
{
cout << "red不属于第一个集合" << endl;
}
if (colour1 <= colour2)
{
cout << "集合一包含于集合二" << endl;
}
else
{
cout << "集合一不包含于集合二" << endl;
}
}
int main()
{
test1();
return 0;
}
测试用例与结果