当前位置:   article > 正文

【C++】面向对象程序设计——实例探讨_c++面向对象编程实例

c++面向对象编程实例

一、栈类型的实现(LIFO——后进先出)

栈类型的成员: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里的相关库函数)

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++;
    }
}

五、重载操作符new与delete来管理某类对象的堆空间(用链表)

#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;

七、利用一个线性表类实现一个队列类(FIFO——先进先出)

线性表类是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);
}

八、用抽象类实现一个抽象数据类型Stack

虚函数(通过引用或指针来访问对象类的虚函数时进行动态绑定):

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
}

九、C++中类属函数的实现(以排序函数为例)

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类型数组进行排序

十、C++中类属类的实现(以类属栈类为例)

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类型元素所构成的栈对象
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/512365
推荐阅读
相关标签
  

闽ICP备14008679号