赞
踩
栈类型的成员:buffer(栈中元素,用数组表示),top(能增加/删除元素的那一端位置)
栈类型的操作:push(进栈,在栈中增加一个元素),pop(退栈,在栈中删除一个元素
1. 直接基于数据表示操作栈类型数据
const int STACK_SIZE = 100; struct Stack { int top; int buffer[STACK_SIZE]; }; Stack st; st.top = -1;//初始化 //把12放进栈 if (st.top == STACK_SIZE - 1) { cout << "Overflow!" << endl; exit(-1); } st.top++; st.buffer[st.top] = 12; //把栈顶元素退栈并存入变量x int x; if (st.top == -1) { cout << "Empty!" << endl; exit(-1); } x = st.buffer[st.top]; st.top--;
2. 基于过程抽象(函数)
void init(Stack& s)//初始化函数 { s.top = -1; } bool push(Stack& s, int i)//进栈函数 { if (s.top == STACK_SIZE - 1) { cout << "Overflow!" << endl; return false; } else { s.top++; s.buffer[s.top] = i; return true; } } bool pop(Stack& s, int& i)//退栈函数 { if (s.top == -1) { cout << "Empty!" << endl; return false; } else { i = s.buffer[s.top]; s.top--; return true; } }
3. 数据抽象:用数组进行实现
const int STACK_SIZE = 100; class Stack { public://外部可见的内容,即对外的接口 Stack() { top = -1 }//构造函数 bool push(int i) { if (top == STACK_SIZE - 1) { cout << "Overflow!" << endl; return false; } else { top++; buffer[top] = i; return true; } } bool pop(int &i) { if (top == -1) { cout << "Empty!" << endl; return false; } else { i = buffer[top]; top--; return true; } } private://隐藏的内容,外部不可见 int top; int buffer[STACK_SIZE]; }
4. 数据抽象:用链表进行实现
#include <iostream> using namespace std; class Stack { public: Stack() { top = NULL; } bool push(int i); bool pop(int& i); private: struct Node { int content; Node* next; }; Node* top; }; bool Stack::push(int i) { Node* p = new Node; if (p == NULL) { cout << "Overflow!" << endl; return false; } else { p->content = i; p->next = top; top = p; return true; } } bool Stack::pop(int& i) { if (top == NULL) { cout << "Empty!" << endl; return false; } else { Node* p = top; top = top->next; i = p->content; delete p; return true; } }
cstring标准库中字符串类函数作用举例(假设函数运行都是基于s1与s2的初值):
#include <cstring> s1 = "ABCD"; s2 = 'abcde'; cout << strlen(s1); // 4 cout << strlen(s2); // 5 strcpy(s1, s2); cout << s1; // abcde strcpy(s1, s2, 3); cout << s1; // abcD strcat(s1, s2); cout << s1; // ABCDabcde strcat(s1, s2, 3); cout << s1; // ABCDabc cout << strcmp(s1, s2); // 1 cout << strcmp(s1, "ABCD"); // 0 cout << strcmp(s2, s1); // -1
#include <cstring> #include <cstdlib> #include <iostream> using namespace std; class String { char* str; public: String()//构造函数 { str = NULL; } String(const char* p)//构造函数 { str = new char[strlen(p) + 1]; strcpy(str, p); } ~String()//析构函数 { delete[]str; str = NULL; } int length()//取字符串长度的成员函数 { return strlen(str); } char& char_at(int i)//返回指定位置上字符的引用的成员函数 { if (i<0 || i>strlen(str)) { cerr << "超出字符串范围!" << endl; exit(-1); } return str[i]; } const char* get_str()//返回字符串指针的成员函数 { return str; } String& copy(const char* p)//复制字符串指针的成员函数 { delete[]str; str = new char[strlen(p) + 1]; strcpy(str, p); return *this; } String& copy(const String& s)//复制字符串引用的成员函数 { return copy(s.str); } String& append(const char* p)//拼接字符串指针的成员函数 { char* p1 = new char[strlen(str) + strlen(p) + 1]; strcpy(p1, str); strcat(p1, p); delete[]str; str = p1; return *this; } String& append(const String& s)//拼接字符串引用的成员函数 { return append(s.str); } int compare(const char* p)//比较字符串指针的成员函数 { return strcmp(str, p); } int compare(const String& s)//比较字符串引用的成员函数 { return strcmp(str, s.str); } }; int main() { String s1;//调用第一个(默认)构造函数 String s2("abcdefg");//调用第二个构造函数 s1.copy("xyz");//把"xyz"复制到s1中 s2.append(s1);//在s2的字符串后面添加一个由s1表示的子串 for (int i = 0; i < s2.length(); i++)//把s2中的小写字母变成大写字母 { if (s2.char_at(i) >= 'a' && s2.char_at(i) <= 'z') s2.char_at(i) = s2.char_at(i) - 'a' + 'A'; } if (s2.compare("ABCDEFGXYZ") == 0) cout << "OK" << endl; cout << s1.get_str() << endl << s2.get_str() << endl; return 0; //main函数中程序的运行结果是: //OK //xyz //ABCDEFGXYZ }
注1:在上面的类String中,之所以把copy和append两个成员函数的返回类型指定为 String& 并在函数中返回 *this,是为了能以下面的方式操作String类的对象s:
s.copy(...).append(...).append(...); //其中的一系列操作都是作用在对象s上的
注2:为保证正确性与安全性,应该定义如下的一个拷贝构造函数:
String::string(const String& s) { str = new char[strlen(s.str)+1]; strcpy(str, s.str); }
注3:利用const成员机制,为防止某些函数对成员的数据修改,可以把String类中的一些获取对象状态的成员函数说明成const成员函数:
class String() { char* str; public: ...... int length() const; char& char_at(int i);//保留原来的char_at函数 char char_at(int i) const;//增加一个用于常量对象的char_at const char *get_str() const; int compare(const char *p) const; int compare(const String& s) const; }
class A { static int obj_count;//记录创建的对象数 ...... public: A() { obj_count++; ...... } A(const A& a) { obj_count++; ...... }//两种构造函数 ~A() { obj_count--; ...... }//析构函数 static int get_num_of_objects() { return obj_count; }//获取对象的个数 ...... }; int A::obj_count = 0;//把创建的对象数初始化为0,注意:静态数据成员的初始化要在类外! ...... cout << A::get_num_of_objects() << endl;//在程序运行某时刻获得A类对象的数目,注意:此处可以直接通过类名空间访问类的静态成员,即“类也是对象”
#include <iostream> #include <cstdlib> using namespace std; class Matrix //矩阵类 { int* p_data; //表示矩阵数据 int row, col; //表示矩阵行数和列数 public: Matrix(int r, int c) //结构函数 { if (r <= 0 || c <= 0) { cerr << "矩阵尺寸不合法!" << endl; exit(-1); } row = r; col = c; p_data = new int[row * col]; } ~Matrix() { delete[]p_data; } int& element(int i, int j) //访问矩阵元素->用于赋值 { if (i < 0 || i >= row || j < 0 || j >= col) { cerr << "下标越界" << endl; exit(-1); } return *(p_data + i * col + j); } int element(int i, int j) const //访问矩阵元素(为常量对象而提供) { if (i < 0 || i >= row || j < 0 || j >= col) { cerr << "下标越界" << endl; exit(-1); } return *(p_data + i * col + j); } int dimension_row() const //获得矩阵的行数 { return row; } int dimension_column() const //获得矩阵的列数 { return col; } void display() const //显示矩阵元素 { int* p = p_data; for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { cout << *p << " "; p++; } cout << endl; } } }; class Vector //向量类 { int* p_data;//表示向量数据 int num;//表示向量的尺寸 public: Vector(int n) //构造函数 { if (n <= 0) { cerr << "向量尺寸不合法!" << endl; exit(-1); } num = n; p_data = new int[num]; } ~Vector() //析构函数 { delete[]p_data; } int& element(int i) //访问向量元素->用于赋值 { if (i < 0 || i >= num) { cerr << "向量下标越界!" << endl; exit(-1); } return *(p_data + i); } int element(int i) const //访问向量元素(为常量对象而提供) { if (i < 0 || i >= num) { cerr << "向量下标越界!" << endl; exit(-1); } return *(p_data + i); } int dimension() const //返回向量的尺寸 { return num; } void display() const //显示向量元素 { int* p = p_data; for (int i = 0; i < num; i++, p++) cout << *p << " "; cout << endl; } }; void multiply(const Matrix& m, const Vector& v, Vector& r)//矩阵m与向量v相乘的函数,返回值为r { if (m.dimension_column() != v.dimension() || m.dimension_row() != r.dimension()) { cerr << "矩阵与向量的尺寸不匹配!" << endl; exit(-1); } int row = m.dimension_row(), col = m.dimension_column(); for (int i = 0; i < row; i++) { r.element(i) = 0; for (int j = 0; j < col; j++) r.element(i) += m.element(i, j) * v.element(j); } } int main() { int row, column; cout << "请输入矩阵的行数和列数:"; cin >> row >> column; Matrix m(row, column); Vector v(column); Vector r(row); cout << "请输入矩阵元素:" << endl; for (int i = 0; i < row; i++) for (int j = 0; j < column; j++) cin >> m.element(i, j); cout << "请输入向量元素:" << endl; for (int i = 0; i < column; i++) cin >> v.element(i); multiply(m, v, r); cout << "矩阵:" << endl; m.display(); cout << "与向量:" << endl; v.display(); cout << "的乘积为:" << endl; r.display(); return 0; }
注:上述的函数multiply中多次通过调用成员函数element访问m, v, r的元素,每一次调用都要监察处下标的合法性,因此效率不高。
如果把函数multiply作为类Matrix和Vector的友元函数,在函数multiply中直接存取它们的私有成员,将会大大提高效率:
class Vector;//先提前声明一下,因为后面要用 class Matrix { ...... friend void multiply(const Matrix& m, const Vector& v, Vector& r); } class Vector { ...... friend void multiply(const Matrix& m, const Vector& v, Vector& r); } void multiply(const Matrix& m, const Vector& v, Vector& r) { if (m.col != v.num || m.row != r.num) { cerr << "矩阵与向量的尺寸不匹配!" <, endl; exit(-1); } int* p_m = m.p_data,; int* p_r = r.p_data,; int* p_v; for (int i = 0; i < m.row; i++) { *p_r = 0; p_v = v.p_data; for (int j = 0; j < m.col; j++) { *p_r += (*p_m) * (*p_v); p_m++; p_v++; } p_r++; } }
#include <string> #include <cstdlib> const int NUM = 32; class A { ......//类A的已有成员说明 public: static void* operator new(size_t, size); static void operator delete(void *p); private: static A* p_free;//用于指向A类对象的自由空间链表 A* next; }; A* A::p_free = NULL; void* A::operator new(size_t, size) { A* p; if (p_free == NULL)//第一次创建对象或上一次申请的空间已用完 { p_free = (A *)malloc(size * NUM);//申请能存储NUM个A类对象的大空间 for (p = p_free; p != p_free + NUM - 1; p++)//建立自由结点链表 p->next = p + 1; p->next = NULL; } //在自由结点链表中为当前创建的对象分配空间 p = p_free; p_free = p_free->next; memset(p, 0, size);//把对象空间初始化全为“0” return p; } void A::operator delete(void* p) { //把p指向的对象空间归还到自由结点链表中 ((A *)p)->next = p_free; p_free = (A *)p }
注:操作符重载的基本格式举例
class Complex{(复数类型)}; (双目 作为成员函数) bool operator == (const Complex& x) const; (双目 作为全局函数: 先要在类中声明友元friend) bool operator == (const Complex& x1, const Complex& x2) const; (单目 作为成员函数) Complex operator -() const; (单目 作为成员函数且后置:带形参int) Complex operator ++(int) const (单目 作为全局函数:先要在类中声明友元friend) Complex operator -(const Complex& x) const; (单目 作为全局函数且后置:先要在类中声明友元friend,且带形参int) Complex operator ++(const Complex& x, int) const;
先学:单继承基类和派生类的基本格式:
class A { protected: int x; public: void f() { ... x ... } }; class B: public A { public: void h() { ... x ... } }
此实例中,公司的部门经理定义为公司普通职员的派生类
class String{ ...... };//String为前面定义的字符串类 class Employee //普通职员类 { String name; int salary; public: Employee(const char *s, int n = 0) :name(s)//:name(s)为成员初始化表 salary = n; void set_salary(int n) { salary = n; } int get_salary() const { return salary; } String get_name() const { return name; } }; const int MAX_NUM_OF_EMPS = 20; class Manager: Public Employee //部门经理类 { Employee *group[MAX_NUM_OF_EMPS];//被管理的职员对象指针数组 int num_of_emps; //被管理的职员人数 public: Manager(const char* s, int n = 0) :Employee(s, n) num_of_emps = 0; Employee* add_employee(Employee* e)//增加一个被管理的职员 { if (num_of_emps >= MAX_NUM_OF_EMPS) return NULL; group[num_of_emps] = e; num_of_emps++; return e; } Employee* remove_employee(Employee* e)//删除一个被管理的职员 { int i; for (i = 0; i < num_of_emps; i++) if (e->get_name() == group[i]->get_name()) break; if (i < num_of_emps) { for (int j = i + 1; j < num_of_emps; j++) group[j - i] = group[j]; num_of_emps--; return e; } else return NULL; } }; ... Employee e1("Jack", 1000); Manager m("Mark", 4000); m.add_employee(&e1); cout << m.get_salary() << endl;
线性表类是n个具有相同特性的数据元素的有限序列,类似于数组,表现了数据元素之间一对一的关系,常用的函数如下面的Linear_list类所示
队列是一种特殊的线性表,对队列只能实施两种操作:进队(在线性表的一端加入元素)和出队(在线性表的另一端删除一个元素)
class Linear_list //线性表 { ...... public: bool insert(int x, int pos);//在线性表中位置pos后面增加元素,当pos为0时,在表头增加元素,返回值表示操作成功或失败 bool remove(int& x, int pos);//删除线性表中位置pos处的元素,返回值表示操作成功或失败 int element(int pos) const;//返回位置pos处的函数 int search(int x) const;//查找值为x的函数,返回元素的位置;未找到时返回0 int length() const;//返回元素个数 } class Queue: private Linear_list //队列,此处的继承方式private可省略 { public: bool en_queue(int x)//元素进队 return insert(x, length()); bool de_queue(int& x)//元素出队 return remove(x,1); }
虽然在上面的程序中把Queue类定义为Linear_list类的派生类,但由于能对线性表实施的操作并不都能实施到队列上,比如,不能在队列的任意位置上增加元素,因此这里的继承方式采用了private继承。private继承属于纯代码复用,而这种复用采用下面的聚集也能实现:
class Queue { Linear_list list; public: bool en_queue(int i) return list.insert(i, list.length()); bool de_queue(int i) return list.remove(i, 1); }
虚函数(通过引用或指针来访问对象类的虚函数时进行动态绑定):
virtual void f();
纯虚函数(只给出函数声明而没给出实现的虚成员函数)→包含纯虚函数的类称为抽象类
virtual int f() = 0;
对于一个栈类型的数据对象s和一个需要栈类型数据作为参数的函数f: void f(T s),如何设计T类型,使得f既能接受用数组实现的栈类型Array_stack类的对象,又能接受用链表实现的栈类型Linked_stack类的对象?
C++给出的解法是:给这两个类定义一个抽象的基类型Stack,在该抽象基类中提供两个纯虚函数push和pop,使Stack, Array_stack和Linked_stack三者构成一个继承关系
class Stack { public: virtual bool push(int i) = 0; virtual bool pop(int& i) = 0; }; class Array_stack : public Stack { int elements[100], top; public: Array_stack() { top = -1; } bool push(int i) { ...... } bool pop(int& i) { ...... } }; class Linked_stack : public Stack { struct Node { int content; Node* next; } *first; public: Linked_stack() { first == NULL }; bool push(int i) { ...... } bool pop(int& i) { ...... } }; void f(Stack* p)//这样,上面函数f的参数类型T就可以写成:"Stack&"或"Stack*",在函数f中通过虚函数的动态绑定来实现对实参对象类(Array_stack或Linked_stack)的push和pop的正确调用。 { ...... p->push(...);//将根据p所指的对象类来确定push的归属 ...... p->pop(....);//将根据p所指的对象类来确定pop的归属 ...... } int main() { Array_stack s1; Linked_stack s2; f(&st1); //OK f(&st2); //OK }
1. 用指针参数实现类属函数
void sort(void* base, //需排序的数据首地址 unsigned int count, //数据元素的个数 unsigned int element_size, //数据元素的尺寸 bool (*less_than)(const void*, const void*) //比较两个数组元素大小的函数指针,由调用者提供) { /* 无论采用何种排序算法,一般都需要对数组进行以下操作: 1. 取第i个元素(i可以从0到count-1),可以由下面的公式计算第i个元素的首地址: (char *)base + i * element_size 2. 比较第i个和第j个元素的大小(i和j可以从0到count-1)。可以先计算出第i个和第j个元素的首地址,然后调用cmp指向的函数来比较这两个地址上的元素的大小(第i个元素是否小于第j个元素)。 (*less_than)((char *)base + i * element_size, (char *)base + j * element_size) 3. 交换第i个和第j个元素。可以先计算出第i个和第j个元素的首地址,然后逐个字节交换这两个地址上的元素。 char* p1 = (char *)base + i * element_size; char* p2 = (char *)base + j * element_size; for (int k = 0; k < element_size; k++) { char temp = p1[k]; p1[k] = p2[k]; p2[k] = temp; } */ }
下面的程序片段是利用上面定义的排序函数分别对int、double以及A类型的数组进行排序:
bool int_less_than(const void* p1, const void* p2) { if (*(int*)p1 < *(int*)p2) return true; else return false; } bool double_less_than(const void* p1, const void* p2) { if (*(double*)p1 < *(double*)p2) return true; else return false; } bool A_less_than(const void* p1, const void* p2) { if (*(A*)p1 < *(A*)p2) //类A中需重载操作符operator < return true; else return false; }
2. 用函数模板实现类属函数
template <class T> //T为元素类型,输入时可以为int、double、A、... void sort(T elements[], unsigned int count) { /* 1. 取第i个元素 elements[i] 2. 比较第i个和第j个元素的大小 elements[i] < elements[j] 3. 交换第i个和第j个元素 T temp = elements[i]; elements[i] = elements[j]; elements[j] = temp; */ } ...... int a[100]; sort(a, 100);//对int类型数组进行排序 double b[200]; sort(b, 200);//对double类型数组进行排序 A c[300];//类A中需重载操作符operator < ,需要时还要自定义拷贝函数和重载操作符operator = sort(c, 300);//对A类型数组进行排序
1. 定义一个可以表示各种类型元素的类属栈类
template <class T> class Stack { T buffer[100]; int top; public: Stack() { top = -1; } bool push(const T& x); bool pop(T& x); } template <class T> bool Stack <T>::push(const T& x) { ... } template <class T> bool Stack <T>::pop(T& x) { ... } ...... Stack<int> st1;//创建一个元素为int类型的栈对象 int x; st1.push(10); st1.pop(x); Stack<double> st2;//创建一个元素为double类型的栈对象 double y; st2.push(1.2); st2.pop(x); Stack<A> st3;//创建一个元素为A类型的栈对象 A a,b; st3.push(a); st3.pop(b);
2. 定义一个能表示不同大小的栈模板
template <class T, int size> class Stack { T buffer[size]; int top; public: Stack() { top = -1; } bool push(const T& x); bool pop(T& x); } template <class T, int size> bool Stack <T>::push(const T& x) { ... } template <class T, int size> bool Stack <T>::pop(T& x) { ... } ...... Stack<int, 100> st1;//创建一个最多由100个int型元素所构成的栈对象 Stack<double, 200> st2;//创建一个最多由200个double类型元素所构成的栈对象
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。