#Chapter 5: object oriented programming style

Basic knowledge

The inheritance mechanism defines the parent-child relationship. The parent class defines the common public interface and private implementation for all children. Each subclass can add or override inherited things to achieve its own unique behavior. In C + +, the parent class is called base class, and the child class is called derived class. The relationship between the parent and the child is called the inheritance hierarchy.

Polymorphism: allows the pointer or reference of the base class to transparently point to an object of any of its derived classes. Before program execution, it has resolved which function should be called, which is called static binding. However, in object-oriented programming methods, the compiler cannot know which function will be called, and this parsing operation will be delayed until run-time, which is called dynamic binding.

The first step of defining an abstract class is to find out the common operation behaviors of all subclasses, and then try to find out which operation behaviors are type dependent, that is to say, which operation behaviors must be implemented in different ways according to different derived classes. These operation behaviors should be virtual function s in the whole inheritance system.

When designing abstract base classes, we need to find out the access level of each operation behavior. If an operation behavior should be accessible to ordinary programs, we should declare it as public; but if an operation behavior does not need to be used outside the base class, we will declare it as private. Even the derived class of the base class cannot access the private in the base class Member: an access level is protected. This level behavior can be accessed by derived classes and not allowed by general programs.

Each virtual function must have its own definition, or it can be set as "pure virtual function". If this virtual function has no real meaning for this class, assign the virtual function to 0, which means it is a pure virtual function. If any class declares one (or more) pure virtual functions, the program cannot generate any objects for it due to the incompleteness of its interface (pure virtual functions have no function definition, which is called incompleteness). Such classes can only be used as subobject s of derived classes, provided that these derived classes must provide accurate definitions for all virtual functions. In addition, according to the general rule, if a base class definition has one (or more) virtual functions, its destructor should be declared as virtual.

The derived class consists of two parts: one is the sub object of the base class, which is composed of the non static data member of the base class, if any; the other is the part of the derived class (which is composed of the non of the derived class

-static data member). The definition of the base class of a class must already exist before an inheritance declaration can be made.

If the data member is a reference, it must be initialized in the member initialization list of the constructor. Once initialized, you can no longer point to another object. If the data member is a pointer, there is no limit: we can initialize it in the constructor, or we can initialize it to null first, and then point it to a valid memory address later. In the process of programming, we decide to use reference or pointer according to these different properties.

When we define a derived class, we must decide whether we want to overwrite the virtual function in the base class or inherit it intact. If we inherit the pure virtual function, the derived class will also be regarded as an abstract class, and no object can be defined for it. If we decide to override the virtual function provided by the base class, the new definition provided by the derived class must run in full accordance with the function prototype declared by the base class, including: parameter list, return type, const ness. In addition, it is not necessary to add the keyword virtual in the declaration operation. The compiler will decide whether a function will overwrite the function with the same name in its base class according to the prototype declaration of two functions.

Exercise answer

Exercise 5.1 implement a two-tier stack class system. Its base class is a pure abstract class stack, which only provides the simplest interfaces: pop(), push(), size(), empty(), full(), peek(), and print(). The two derived classes are LIFO stack and peekback stack. Peekback_Stack() allows the user to access any element without changing the stack element.

#include <iostream>
#include <string>
#include <vector>

using namespace std;

typedef string elemType;

class Stack
{
public:
    virtual ~Stack(){}
    virtual bool pop(elemType&) = 0;
    virtual bool push(const elemType&) = 0;
    virtual bool peek(int index, elemType&) = 0;
    virtual int top() const = 0;
    virtual int size() const = 0;
    virtual bool empty() const = 0;
    virtual bool full() const = 0;
    virtual void print(ostream& = cout) const = 0;
};

ostream& operator<<(ostream& os, const Stack& rhs)
{
    rhs.print();
    return os;
}

class LIFO_Stack :public Stack
{
public:
    LIFO_Stack(int capacity = 0) :_top(0)
    {
        if (capacity)
            _stack.reserve(capacity);
    }
    int size() const { return _stack.size(); }
    bool empty()const { return !_top; }
    bool full() const { return size() >= _stack.max_size(); }
    int top() const { return _top; }
    void print(ostream& os = cout) const;
    bool pop(elemType& elem);
    bool push(const elemType& elem);
    bool peek(int, elemType&) { return false; }

private:
    vector<elemType> _stack;
    int _top;
};

bool LIFO_Stack::pop(elemType& elem)
{
    if (empty()) return false;
    elem = _stack[--_top];
    _stack.pop_back();
    return true;
}

bool LIFO_Stack::push(const elemType& elem)
{
    if (full())    return false;
    _stack.push_back(elem);
    ++_top;
    return true;
}

void LIFO_Stack::print(ostream& os) const
{
    vector<elemType>::const_reverse_iterator rit = _stack.rbegin(),
        rend = _stack.rend();
    os << "\n\t";
    while (rit != rend)
    {
        os << *rit++ << "\n\t";
    }
    os << endl;
}

class Peekback_Stack :public Stack
{
public:
    Peekback_Stack(int capacity = 0) :_top(0)
    {
        if (capacity)
            _stack.reserve(capacity);
    }
    int size() const { return _stack.size(); }
    bool empty()const { return !_top; }
    bool full() const { return size() >= _stack.max_size(); }
    int top() const { return _top; }
    void print(ostream& os = cout) const;
    bool pop(elemType& elem);
    bool push(const elemType& elem);
    bool peek(int, elemType&);
private:
    vector<elemType> _stack;
    int _top;
};

bool Peekback_Stack::pop(elemType& elem)
{
    if (empty()) return false;
    elem = _stack[--_top];
    _stack.pop_back();
    return true;
}

bool Peekback_Stack::push(const elemType& elem)
{
    if (full())    return false;
    _stack.push_back(elem);
    ++_top;
    return true;
}

void Peekback_Stack::print(ostream& os) const
{
    vector<elemType>::const_reverse_iterator rit = _stack.rbegin(),
        rend = _stack.rend();
    os << "\n\t";
    while (rit != rend)
    {
        os << *rit++ << "\n\t";
    }
    os << endl;
}

bool Peekback_Stack::peek(int index, elemType& elem)
{
    if (empty())
        return false;
    if (index < 0 || index >= size())
        return false;
    elem = _stack[index];
    return true;
}

//non-member function peek()Accept an "abstract class" Stack Of reference"As a parameter,
//And call the Stack Virtual function of object peek()-This virtual function is unique to all derived classes.
void peek(Stack& st, int index)
{
    cout << endl;
    string t;
    if (st.peek(index, t))
        cout << "peek: " << t;
    else
        cout << "peek failed!";
    cout << endl;
}

int main()
{
    LIFO_Stack st;
    string str;
    while (cin >> str && !st.full())
        st.push(str);
    cout << '\n' << "About to call peek() with LIFO_Stack" << endl;
    peek(st, st.top() - 1);
    cout << st;
    
    Peekback_Stack pst;
    while (!st.empty())
    {
        string t;
        if (st.pop(t))
            pst.push(t);
    }
    cout << "About to call peek() with Peekback_Stack" << endl;
    peek(pst, pst.top() - 1);
    cout << pst;

    return 0;
}

end.

"Erudite and prudent, discerning and practicing."

Keywords: C++ Programming

Added by davidz on Fri, 14 Feb 2020 10:56:33 +0200