在C++中写出构造、析构函数等,实现顺序表的增删查改操作。
下面是头文件,类函数的实现。
#pragma once typedef int DataType; class SeqList { public: SeqList(); //顺序表只考虑深拷贝 //传统写法 //SeqList(const SeqList& s); //SeqList& operator=(const SeqList& s); //现代写法 SeqList(DataType* array, const size_t size); SeqList(const SeqList& s); SeqList& operator=(SeqList& s); ~SeqList(); void _CheckCapacity(); void PushBack(DataType x); void PopBack(); void PushFront(DataType x); void PopFront(); void Insert(size_t pos, DataType x); void Erase(size_t pos); size_t Find(DataType x); void PrintSepList(); private: DataType* _array; size_t _size; size_t _capacity; };
构造函数、运算符重载、析构函数、增删查改等函数实现如下:
#include<iostream> using namespace std; #include"SeqList.h" SeqList::SeqList() :_array(NULL) , _size(0) , _capacity(0) {} //顺序表只考虑深拷贝 //传统写法 //SeqList(const SeqList& s) // :_array(new DataType[s._size]) // ,_size(s._size) // ,_capacity(s._capacity) //{//内存拷贝,strcpy和strncpy针对char* // memcpy(_array, s._array, sizeof(DataType)*(_size)); //} //SeqList& operator=(const SeqList& s) //{ // if (this != &s) // { // DataType* tmp = new DataType[s._size]; // delete[] _array; // _array = tmp; // _size = s._size; // _capacity = s._capacity; // memcpy(_array, s._array, sizeof(DataType)*(_size)); // } // return *this; //} //现代写法 SeqList::SeqList(DataType* array, const size_t size) :_array(new DataType[size]) , _size(size) , _capacity(size) { memcpy(_array, array, sizeof(DataType)*size); } SeqList::SeqList(const SeqList& s) :_array(NULL) { SeqList tmp(s._array, s._size); swap(_array, tmp._array); _size = s._size; _capacity = s._size;//容量用_size,防止越界 } SeqList& SeqList::operator=(SeqList& s) { if (this != &s) { swap(_array, s._array); _size = s._size; _capacity = s._capacity; } return *this; } SeqList::~SeqList() { if (_array) { delete[] _array; } } void SeqList::_CheckCapacity()//检查容量 { if (_size >= _capacity) { _capacity = 2 * _capacity + 3;//_capacity是否为0 _array = (DataType*)realloc(_array, _capacity*sizeof(DataType));//realloc扩容,后面用字节数表示 } } void SeqList::PrintSepList()//打印顺序表 { for (int i = 0; i < (int)_size; i++) { cout << _array[i] << "-"; } cout << "NULL" << endl; } void SeqList::PushBack(DataType x)//尾插 { _CheckCapacity(); _array[_size++] = x; } void SeqList::PopBack()//尾删 { if (_size > 0) { _array[_size--] = NULL; } else cout << "SeqList is empty" << endl; } void SeqList::PushFront(DataType x)////头插 { _CheckCapacity(); for (int i = (int)_size; i >0 ; i--) { _array[i] = _array[i - 1]; } _array[0] = x; _size++; } void SeqList::PopFront()//头删 { if (_size > 0) { _array[0] = NULL; for (int i = 0; i < (int)_size; i++) { _array[i] = _array[i + 1]; } _size--; } else cout << "SeqList is empty" << endl; } void SeqList::Insert(size_t pos, DataType x)//指定位置处插入一个数 {//检查pos的有效性 if (pos <= 0 || pos > _size + 1) { cout << "position is error!" << endl; } else if (pos == _size) SeqList::PushBack(x); else { for (int i = (int)(++_size); i > ((int)pos - 1); i--) { _array[i] = _array[i - 1]; } _array[pos - 1] = x; } } void SeqList::Erase(size_t pos)//删除指定位置 { if (pos <= 0 || pos > _size + 1) { cout << "position is error!" << endl; } else if (pos == _size) SeqList::PopBack(); else { _array[pos - 1] = NULL; for (int i = pos - 1; i < (int)_size; i++) { _array[i] = _array[i + 1]; } _size--; } } size_t SeqList::Find(DataType x)//查找某数 { int i; for (i = 0; i < (int)_size; i++) { if (x == _array[i]) return i + 1; } if (i == (int)_size) return 0; return -1; }
测试用例如下:
void Test1() {//尾插尾删 SeqList S; S.PushBack(1); S.PushBack(2); S.PushBack(3); S.PushBack(4); S.PushBack(5); S.PrintSepList(); S.PopBack(); S.PopBack(); S.PrintSepList(); SeqList S1(S); S1.PrintSepList(); SeqList S2; S2.PrintSepList(); S2 = S1; S2.PrintSepList(); } void Test2() {//头插头删 SeqList S; S.PushFront(1); S.PushFront(2); S.PushFront(3); S.PushFront(4); S.PushFront(5); S.PrintSepList(); S.PopFront(); S.PopFront(); S.PrintSepList(); } void Test3() {//指定位置处插入一个数 SeqList S; S.PushBack(1); S.PushBack(2); S.PushBack(4); S.PushBack(5); S.PrintSepList(); S.Insert(3, 3); S.PrintSepList(); S.Insert(5, 3); S.PrintSepList(); S.Insert(7, 3); S.PrintSepList(); } void Test4() {//删除指定位置 SeqList S; S.PushBack(1); S.PushBack(2); S.PushBack(3); S.PushBack(8); S.PushBack(4); S.PushBack(5); S.PrintSepList(); S.Erase(4); S.PrintSepList(); S.Erase(6); S.PrintSepList(); S.Erase(8); S.PrintSepList(); } void Test5() {//查找某数 SeqList S; S.PushBack(1); S.PushBack(2); S.PushBack(3); S.PushBack(4); S.PushBack(5); S.PrintSepList(); cout << S.Find(3) << endl; cout << S.Find(6) << endl; }
构造函数、拷贝构造函数、析构函数和赋值函数都是编译器默认函数,但有时需要手动重写这些函数,下面是对这些函数进行介绍。
一. 构造函数
1. 构造函数是一种用于创建对象的特殊成员函数,当创建对象时,系统自动调用构造函数。
2. 构造函数名与类名相同,一个类可以拥有多个构造函数(重载),构造函数可以有任意类型的参数,但不能具有返回类型,连Void也不可以,它有隐含的返回值,该值由系统内部使用。
3. 构造函数的作用是:为对象分配空间;对数据成员赋初值;请求其他资源。
4. 如果一个类没有定义构造函数,编译器会自动生成一个无参的默认构造函数。
二. 析构函数
1. 析构函数名字为符号“~”加类名,析构函数没有参数和返回值。
2. 一个类中只可能定义一个析构函数,所以析构函数不能重载。
3. 析构函数的作用是进行清除对象,释放内存等。当对象超出其定义范围时(即释放该对象时),编译器自动调用析构函数。
在以下情况下,析构函数也会被自动调用:
(1)如果一个对象被定义在一个函数体内,则当这个函数结束时,该对象的析构函数被自动调用。
(2)若一个对象是使用new运算符动态创建的,在使用delete运算符释放它时,delete将会自动调用析构函数。用new创建的对象,必须用delete销毁。
4. delete释放new产生的空间,而delete [] 释放new []产生的数组空间。
三. 拷贝构造函数
1. 当构造函数的参数为自身类的引用时,这个构造函数称为拷贝构造函数。拷贝构造函数的功能是用一个已有对象初始化一个正在建立的同类对象。
2. 拷贝构造函数的特点如下:
(1)该函数名与类同名,因为它也是一种构造函数,并且该函数也不被指定返回类型;
(2)该函数只有一个参数,并且是对某个对象的引用;
(3)每个类都必须有一个拷贝构造函数;
(4)如果程序员没有显式地定义一个拷贝构造函数,那么,C++编译器会自动生成一个缺省的拷贝构造函数.
(5)拷贝构造函数的目的是建立一个新的对象实体,所以,一定要保有证新创建的对象有着独立的内存空间,而不是与先前的对象共用。
在定义一些类时,有时需要(而且强立推荐)显式地定义拷贝构造函数。
3.拷贝构造函数主要在如下三种情况中起初始化作用。
(1)声明语句中用一个对象初始化另一个对象(一定是在"声明并初始化对象"时被调用);例如TPoint P2(P1)表示由对象P1初始化P2时,需要调用拷贝构造函数。
(2)将一个对象作为参数按值调用方式(而不是指针)传递给另一个对象时生成对象副本(即 复制构造函数在"对象作为函数参数"时被调用)。当对象作为函数实参传递给函数形参时,如p=f(N),在调用f()函数时,对象N是实参,要用它来初始化被调用函数的形参,这时需要调用拷贝构造函数。
(3)生成一个临时的对象作为函数的返回结果。但对象作为函数返回值时,如return R时,系统将用对象R来初始化一个匿名对象,这时需要调用拷贝构造函数。
4.拷贝构造函数的执行:
(1)用已有对象初始化创建对象。
(2)当对象作函数参数时,因为要用实参初始化形参,也要调用拷贝构造函数。
(3)函数返回值为类类型时,情况也类似。
四. 赋值函数
1. 赋值操作符则给对象一个新的值,既然是新的值,说明这个对象原来就有值,这也说明,赋值函数只能被已经存在了的对象调用。
2. 如果不主动编写拷贝构造函数和赋值函数,编译器将以“位拷贝”的方式自动生成缺省的函数。倘若类中含有指针变量,那么这两个缺省的函数就隐含了错误。