C++初学者指南 第九篇(12)

工程9-1 建立集合类ios

运算符重载使得咱们能够建立与C++编程环境彻底兼容的类。例如, 经过定义必要的运算符函数,咱们能够像使用内置数据类型那样使用类。咱们能够对该类的对象使用运算符,还能够在表达式中使用该类的对象。下面咱们经过建立一个名为Set的类,定义Set类型来演示如何建立与C++编程环境想兼容的类。编程

在开始以前,咱们必须先明确这里说的Set的具体含义。在这个工程中,Set(集合)被定义是一系列互不相同的元素的集合。也就是说,在任何一个给定的Set(集合)中没有两个相同的元素。集合中元素的顺序是可有可无的。所以集合{A,B,C}{A,C,B}是同一个集合。集合还能够为空。数组

集合支持不少的操做。本工程中实现了集合的下列操做:函数

为集合增长元素this

从集合中删除一个元素spa

集合的并集指针

集合的差集code

其中,增长元素到集合中和从集合中删除元素操做的含义是很明显的。这里须要对并集和差集进行解释。两个集合的并集就是含有这两个集合所有元素的集合。(固然,其中不含有重复的元素)。咱们将使用+运算来完成并集的运算。对象

集合的差集是仅含有第一个集合中那些不属于第二个集合的元素构成的集合。咱们将使用-运算来实现集合的差集。例如,有两个集合S1和S2把S2从S1中删除后的集合构成S3的语句以下:索引

S3 = S1 - S2;

若是S1和S2中的元素是彻底相同的,则S3集合为空的集合。类Set中还有一个成员函数为isMember(),用来判断一个元素是否属于该集合。固然,还有一些别的和集合相关的运算。其中一些会在练习题中找到,其他的咱们能够尝试本身编写。

为了简单起见,Set类存储的是字符。可是用于存储其它类型元素的类在实现原理上都是相同的。

步骤:

1. 建立一个新文件,名称为Set.cpp.

2. 经过声明Set类,以下:

const int MaxSize = 100;
class Set
{
    int len; //元素的数量
    char members[MaxSize]; //采用数组来存储集合中的元素
    /* find()函数是私有的,由于在Set类以外是不使用的*/
    int find(char ch);  //查找一个元素
public:
    //构造一个空的集合
    Set()
    {
        len = 0;
    };
 
    //返回集合中元素的数量
    int getLength()
    {
        return len;
    }
 
    void showset(); // 显示出集合中的元素
    bool isMember( char ch ); // 检查是不是集合中的元素
 
    Set operator +(char ch); // 为集合增长元素
    Set operator -(char ch); // 删除一个元素
 
    Set operator +(Set ob2); //求集合的并集
    Set operator -(Set ob2); //求集合的差集
};

集合中采用数组members来存储元素。集合中元素的数量保存在len变量中。集合中最多能够含有MaxSize个元素,也就是100个元素。(若是须要处理更大的集合,能够自行修改这个值。)

Set类的构造函数生成一个空的集合,即不含有任何元素。咱们没有必要再建立别的构造函数或者显示地定义其拷贝构造函数了,由于缺省的逐位拷贝的操做是知足咱们要求的。函数getLength()返回len的值,也就是集合中实际的元素的数量。

3. 从私有函数find()开始定义其成员函数,以下:

/* 返回指定元素ch的索引;
   若是没有找到,则返回-1 */
int Set::find(char ch)
{
    int i;
    for ( i = 0; i < len ; i++)
    {
        if ( ch == members[i] )
        {
            return i;
        }
    }
    
    return -1;
}

上面的函数用来判断传入的字符ch是不是集合中的元素。若是在集合中找到了该元素,函数返回其索引;不然返回-1。咱们在类Set范围以外不使用这个函数,所以它是私有的。正如咱们在前面讲过的那样,成员函数能够是仅供该类使用的私有函数。私有函数只能由该类中的其余成员函数来调用。

4. 为类增长showset()函数,以下:

//显示集合中的元素
void Set::showset()
{
    cout << " ( ";
    for( int i = 0; i < len ; i++)
    {
        cout << members[i] << " ";
    }
 
    cout << ")";
}

这个函数显示出集合的内容。

5. 为类增长isMember()函数,用来判断一个字符是否在集合中。以下:

/* 若是指定的字符时集合中的元素则返回true;
   不然返回false */
bool Set::isMember(char ch)
{
    if ( find(ch) != -1 )
    {
        return true;
    }
    else
    {
        return false;
    }
}

这个函数中调用了find()来判断ch是不是集合中的元素。若是是,isMember()函数返回true;不然返回false

6. 下面开始为Set类增长运算符。从加法运算符开始。对Set类的+运算符进行重载,实现往集合中增长一个元素,以下。

//为集合中增长一个元素
Set Set::operator +(char ch)
{
    Set newset;
 
    if ( len == MaxSize )
    {
        cout << "Set is full . \n";
        return *this; 
    }
 
    newset = *this;//复制当前的集合
 
    //检查ch是否已经在集合中了
    if(find(ch) == -1 )
    {
        //若是不在集合中,则增长该元素到集合中
        newset.members[newset.len] = ch;
        newset.len++;
    }
 
    return newset; //返回数据更新后的集合
}

这个函数咱们须要仔细研究一下。首先,生成了一个新的集合。这个集合将用来存储原集合中的元素再加上元素ch。在把ch增长到集合中以前,先检查是否有足够的空间来存储增长的字符。若是有足够的空间可用于存储新增的字符,则把原始的集合赋值给新的集合。接着调用函数find()来判断ch是否已经在集合中了。若是不在,则把ch增长到新的集合中,并让len自增。在各类状况下,函数都是返回newset这个新的集合。所以,原始的集合在该函数中没有被修改,保持不变。

7. 重载-运算,以实现从集合中删除一个元素,以下:

// 从集合中删除一个元素
Set Set::operator -(char ch)
{
    Set newset;
 
    int i = find(ch); //若是ch不在集合中,则i为-1 
 
    //把其它的元素复制到新的集合中
    for ( int j = 0; j < len ; j ++)
    {
        if ( j != i )
        {
            newset = newset + members[j];
        }
    }
 
    return newset;
}
函数一开始建立了一个空的集合。而后调用find()函数来取得ch在原始集合中的索引。find()函数在ch不是集合中元素的时候返回-1。接着,经过循环把原始集合中除了索引等于find()返回值以外的全部元素都加入到新的集合中。这样,新的集合中就含有原始集合中除了ch之外的所有元素了。若是ch不是原始集合中的元素,新的集合和原始集合是相等的。

8. 重载+和-,来求集合的并集和差集,以下:

//求并集
Set Set::operator +(Set ob2)
{
     Set newset = * this; //拷贝第一个集合
 
     //把第二个集合中不属于第一个集合的元素拷贝到新的集合中
     for ( int i = 0; i < ob2.len; i++)
     {
         newset = newset + ob2.members[i];
     }
 
     return newset; //返回并集
}
 
//求差集
Set Set::operator -(Set ob2)
{
 
    Set newset = * this; //拷贝第一个集合
    
    //从中减去第二个集合中的元素
    for ( int i = 0; i < ob2.len; i++)
    {
        newset = newset - ob2.members[i];
    }
 
    return newset; //返回差集
}

咱们能够看到,在这两个函数中咱们用到了前面定义的+和-运算来辅助完成求并集和差集的操做。就求并集的操做来讲,先生成了一个含有第一个集合全部元素的新集合。而后把第二个集合中的元素增长到了新的集合中。因为+运算符只会把不在集合中的元素加入到集合中,所以获得的新的集合就是不含有重复元素的两个集合的并集。Set类的求差集运算符则是把第二个集合中的元素从第一个中删除掉。

9. 下面是Set了以及main()函数构成的演示Set类使用方法的完整程序:

/* 构成-1
   元素为字符的集合类
*/
#include <iostream>
using namespace std;
 
const int MaxSize = 100;
class Set
{
    int len; //元素的数量
    char members[MaxSize]; //采用数组来存储集合中的元素
    /* find()函数是私有的,由于在Set类以外是不使用的*/
    int find(char ch);  //查找一个元素
public:
    //构造一个空的集合
    Set()
    {
        len = 0;
    };
 
    //返回集合中元素的数量
    int getLength()
    {
        return len;
    }
 
    void showset(); // 显示出集合中的元素
    bool isMember( char ch ); // 检查是不是集合中的元素
 
    Set operator +(char ch); // 为集合增长元素
    Set operator -(char ch); // 删除一个元素
 
    Set operator +(Set ob2); //求集合的并集
    Set operator -(Set ob2); //求集合的差集
 
};
 
 
/* 返回指定元素ch的索引;
   若是没有找到,则返回-1 */
int Set::find(char ch)
{
    int i;
    for ( i = 0; i < len ; i++)
    {
        if ( ch == members[i] )
        {
            return i;
        }
    }
    
    return -1;
}
 
//显示集合中的元素
void Set::showset()
{
    cout << " ( ";
    for( int i = 0; i < len ; i++)
    {
        cout << members[i] << " ";
    }
 
    cout << ")";
}
 
/* 若是指定的字符时集合中的元素则返回true;
   不然返回false */
 
bool Set::isMember(char ch)
{
    if ( find(ch) != -1 )
    {
        return true;
    }
    else
    {
        return false;
    }
}
 
//为集合中增长一个元素
Set Set::operator +(char ch)
{
    Set newset;
 
    if ( len == MaxSize )
    {
        cout << "Set is full . \n";
        return *this; 
    }
 
    newset = *this;//复制当前的集合
 
    //检查ch是否已经在集合中了
    if(find(ch) == -1 )
    {
        //若是不在集合中,则增长该元素到集合中
        newset.members[newset.len] = ch;
        newset.len++;
    }
 
    return newset; //返回数据更新后的集合
}
 
// 从集合中删除一个元素
Set Set::operator -(char ch)
{
    Set newset;
 
    int i = find(ch); //若是ch不在集合中,则i为-1 
 
    //把其它的元素复制到新的集合中
    for ( int j = 0; j < len ; j ++)
    {
        if ( j != i )
        {
            newset = newset + members[j];
        }
    }
 
    return newset;
}
 
//求并集
Set Set::operator +(Set ob2)
{
     Set newset = * this; //拷贝第一个集合
 
     //把第二个集合中不属于第一个集合的元素拷贝到新的集合中
     for ( int i = 0; i < ob2.len; i++)
     {
         newset = newset + ob2.members[i];
     }
 
     return newset; //返回并集
}
 
//求差集
Set Set::operator -(Set ob2)
{
 
    Set newset = * this; //拷贝第一个集合
    
    //从中减去第二个集合中的元素
    for ( int i = 0; i < ob2.len; i++)
    {
        newset = newset - ob2.members[i];
    }
 
    return newset; //返回差集
}
 
//展现集合类Set的使用
int main()
{
    //构建空的集合
    Set s1;
    Set s2;
    Set s3;
 
    s1 = s1 + 'A';
    s1 = s1 + 'B';
    s1 = s1 + 'C';
 
    cout << "s1 after adding A B C: ";
    s1.showset();
 
    cout << "/n/n";
 
    cout << "Testing for membership using isMember().\n";
    if ( s1.isMember('B'))
    {
        cout << "B is a member of s1.\n";
    }
    else
    {
        cout << "B is not a member of s1.\n";
    }
 
    if ( s1.isMember('T'))
    {
        cout << "T is a member of s1.\n";
    }
    else
    {
        cout << "T is not a member of s1.\n";
    }
 
    cout << "\n";
    
    s1 = s1 - 'B';
    cout << "s1 after s1 = s1 - 'B' ";
    s1.showset();
 
    cout << "\n";
 
    s1 = s1 - 'A';
    cout << "s1 after s1 = s1 - 'A' ";
    s1.showset();
 
    cout << "\n";
 
    s1 = s1 - 'C';
    cout << "s1 after s1 = s1 - 'C' ";
    s1.showset();
   
    cout << "\n\n";
 
    s1 = s1 + 'A';
    s1 = s1 + 'B';
    s1 = s1 + 'C';
    cout << "s1 after adding A B C: ";
    s1.showset();
 
    cout << "\n\n";
 
    s2 = s2 + 'A';
    s2 = s2 + 'X';
    s2 = s2 + 'W';
    cout << "s2 after adding A X W: ";
    s2.showset();
 
    cout << "\n\n";
 
    s3 = s1 + s2;
    cout << "s3 after s3 = s1 + s2: ";
    s3.showset();
 
    cout << "\n\n";
 
    s3 = s3 - s1;
    cout << "s3 after s3 - s1 : ";
    s3.showset();
 
    cout << "\n\n";
 
    
    s2 = s2 - s2; /*清空s2 */
    cout << "s2 after s2 = s2 - s2: ";
    s2.showset();
 
    cout << "\n\n";
 
    s2 = s2 + 'C';
    s2 = s2 + 'B';
    s2 = s2 + 'A';
 
    cout << "s2 after adding C B A: ";
    s2.showset();
 
    return 0;
}

上面程序的输出结果以下:

s1 after adding A B C:  ( A B C )

 

Testing for membership using isMember().

B is a member of s1.

T is not a member of s1.

 

s1 after s1 = s1 - 'B'  ( A C )

s1 after s1 = s1 - 'A'  ( C )

s1 after s1 = s1 - 'C'  ( )

 

s1 after adding A B C:  ( A B C )

 

s2 after adding A X W:  ( A X W )

 

s3 after s3 = s1 + s2:  ( A B C X W )

 

s3 after s3 - s1 :  ( X W )

 

s2 after s2 = s2 - s2:  ( )

 

s2 after adding C B A:  ( C B A )

 

练习

1. 什么是拷贝构造函数?它在何时被调用?写出拷贝构造函数的通用形式。

2. 请解释一下当函数返回一个对象的时候发生了什么?特别解释析构函数是在何时被调用的。

3. 有以下代码段:

class T
{
    int i,j;
public:
    int sum()
    {
        return i + j;
    }
}
请使用this指针重写上面的代码。

4. 什么是结构体? 什么是联合体?

5. 在类的成员函数中, *this指的什么?

6. 什么是友元函数?

7. 写出重载二目运算符函数的通用形式。

8. 怎样作才能实现涉及类类型和内置类型的运算?

9. ?运算符能否被重载?运算符的优先级别是否能够被改变?

10. 就工程9-1中的Set类,定义< 和 >运算用来判断一个集合是其子集或者是超集。其中,< 运算符在其左侧的集合是其右侧集合的子集的时候返回true;不然返回false。> 运算在其左侧集合是其右侧集合的超级的时候返回true;不然返回false。

11. 为Set类定义&运算,用于计算两个集合的交集。

12. 就工程9-1中的Set类,为其增长其它的运算。好比,增长|运算,用于计算两个集合的对称差分。对称差分是由两个集合中不属于他们交集的那些元素构成的集合。