第5章介绍了C++语言中使用Class进行类与类之间面向对象的编程的方式
导言:单纯的以对象为基础(object-based)的类机制无法针对同一种类之间的共通性质进行系统化的划分。因为这种单纯地以对象为基础的模型无法让我们更进一步地指出类间的关系。类与类之间的关系要使用面向对象编程模型(object-oriented programming model)加以设定。
面向对象编程概念的2项最主要的特质是:继承和多态。前者使我们得以将一群相关的类组织起来,并让我们得以分享其间的共通数据和操作行为,后者让我们在这些类之上进行编程时可以如同操控单一个体,而非相互独立的类,并赋予我们更多弹性来加入或移除任何特定类。
本章作者使用了一个LibMat类来模拟一个抽象基类并以此为基类派生出多个不同的基类,并教导读者在实现继承时父类和子类之间的构造函数和析构函数之间的先后调用关系。后用了一个sequence类派生出多个不同的数列类来模拟多态之间的应用,并且详细地说明了多态指针的应用。
附上笔者所写的课后练习答案。
//ch5_main.cpp
#include <iostream>
#include <string>
using namespace std;
class LibMat
{
public:
LibMat() { cout << "LibMat::LibMat() default constructor!\n"; }
virtual ~LibMat() { cout << "LibMat::~LibMat() destructor!\n"; }
virtual void print() const { cout << "LibMat::print() -- I am a LibMat object!\n"; }
};
class Book : public LibMat
{
public:
Book(const string &title, const string &author) : _title(title), _author(author)
{
cout << "Book::Book(" << _title;
cout << ", " << _author << ") constructor\n";
}
~Book() { cout << "Book::~Book() destructor!\n"; }
virtual void print() const
{
cout << "Book::print() -- I am a Book object!\n";
cout << "My title is: " << _title << '\n';
cout << "My author is: " << _author << endl;
}
const string &title() const { return _title; }
const string &author() const { return _author; }
protected:
string _title;
string _author;
};
class AudioBook : public Book
{
public:
AudioBook(const string &title, const string &author, const string &narrator)
: Book(title, author), _narrator(narrator)
{
cout << "AudioBook::AudioBook(" << _title;
cout << ", " << _author;
cout << ", " << _narrator;
cout << ") constructor\n";
}
~AudioBook() { cout << "AudioBook::~AudioBook() destructor!\n"; }
virtual void print() const
{
cout << "AudioBook::print() -- I am a AudioBook object!\n";
cout << "My title is: " << _title << '\n';
cout << "My author is: " << _author << '\n';
cout << "My narrator is: " << _narrator << endl;
}
const string &narrator() const { return _narrator; }
protected:
string _narrator;
};
void print(const LibMat &mat)
{
cout << "in global print(): about to print mat.print()\n";
mat.print();
}
int main()
{
{
cout << "\nCreating a LibMat object to print()\n";
LibMat m;
print(m);
}
{
cout << "\nCreating a Book object to print()\n";
Book b("The Castle", "Franz Kafka");
print(b);
}
{
cout << "\nCreating a AudioBook object to print()\n";
AudioBook ab("Man Without Qualities", "Robert Musil", "Kenneth Meyer");
print(ab);
}
return 0;
}
//-----------------------------------------------------
//sequence.cpp
#include <iostream>
#include <string>
#include <vector>
#include <typeinfo>
#include <algorithm>
using namespace std;
class num_sequence
{
public:
typedef vector<unsigned int>::iterator iterator;
iterator begin() const { return _relems.begin() + _beg_pos - 1; }
iterator end() const { return _relems.begin() + _beg_pos + _length; }
virtual ~num_sequence() {}
virtual num_sequence *clone() const = 0;
unsigned int elem(int pos) const;
bool is_elem(unsigned int) const;
int pos_elem(unsigned int) const;
const char *what_am_i() const;
static int max_elems() { return _max_elems; }
ostream &print(ostream &os = cout) const;
int length() const { return _length; }
int beg_pos() const { return _beg_pos; }
void set_position(int pos);
void set_length(int pos);
bool operator==(const num_sequence &) const;
bool operator!=(const num_sequence &) const;
const vector<unsigned int> *sequence() const { return &_relems; }
protected:
num_sequence(int, int, vector<unsigned int> &);
enum
{
_max_elems = 1024
};
virtual void gen_elems(int pos) const = 0;
int _calc_pos(unsigned int elem) const;
bool check_integrity(int pos, int size) const;
int _length;
int _beg_pos;
vector<unsigned int> &_relems;
};
ostream &operator<<(ostream &os, const num_sequence &ns)
{
return ns.print(os);
}
inline bool num_sequence::operator==(const num_sequence &rhs) const
{
return _beg_pos == rhs._beg_pos && _length == rhs._length;
}
inline bool num_sequence::operator!=(const num_sequence &rhs) const
{
return !(*this == rhs);
}
inline void num_sequence::set_position(int pos)
{
if (pos <= 0 || pos > _max_elems)
{
cerr << "!! invalid position: " << pos;
cerr << " setting pos to default value of 1\n";
cerr << "If inadequate, invoke set_position(pos)\n";
pos = 1;
}
_beg_pos = pos;
return;
}
inline void num_sequence::set_length(int len)
{
if (len <= 0 || len + _beg_pos - 1 > _max_elems)
{
cerr << "!! invalid length for this object: " << len;
cerr << " setting length to default value of 1\n";
cerr << "If inadequate, invoke set_length(len)\n";
len = 1;
}
_length = len;
return;
}
inline num_sequence::num_sequence(int beg_pos, int len, vector<unsigned int> &re) : _relems(re)
{
set_position(beg_pos);
set_length(len);
}
inline unsigned int num_sequence::elem(int pos) const
{
return !check_integrity(pos, _relems.size()) ? 0 : _relems[pos - 1];
}
inline const char *num_sequence::what_am_i() const
{
return typeid(*this).name();
}
inline bool num_sequence::check_integrity(int pos, int size) const
{
bool status = true;
if (pos <= 0 || pos > _max_elems)
{
cerr << "!! invalid position: " << pos;
cerr << " Cannot honor request\n";
status = false;
}
else if (pos > size)
{
gen_elems(pos);
}
return status;
}
inline bool num_sequence::is_elem(unsigned int elem) const
{
return !check_integrity(_beg_pos, _length) ? false : binary_search(begin(), end(), elem);
}
ostream &num_sequence::print(ostream &os) const
{
int elem_pos = _beg_pos - 1;
int end_pos = elem_pos + _length;
if (!check_integrity(end_pos, _relems.size()))
{
return os;
};
os << "(" << _beg_pos << " , ";
os << _length << ") ";
while (elem_pos < end_pos)
{
os << _relems[elem_pos++] << ' ';
}
return os;
}
int num_sequence::pos_elem(unsigned int elem) const
{
cout << "pos_elem( " << elem << " )\n";
if (_relems[_relems.size() - 1] < elem)
{
return _calc_pos(elem);
}
iterator iter = find(_relems.begin(), _relems.end(), elem);
return iter == _relems.end() ? 0 : distance(_relems.begin(), iter) + 1;
}
int num_sequence::_calc_pos(unsigned int elem) const
{
int pos = _relems.size() - 1;
cout << "calc_pos invoked()!: elem: " << elem;
cout << " pos: " << pos;
cout << " at: " << _relems[pos];
cout << "\n";
while (pos < _max_elems && _relems[pos] < elem)
{
gen_elems(++pos);
cout << "pos: " << pos;
cout << " at: " << _relems[pos] << endl;
}
return (pos < _max_elems && _relems[pos] == elem) ? pos + 1 : 0;
}
class Fibonacci : public num_sequence
{
public:
Fibonacci(int beg_pos = 1, int len = 1) : num_sequence(beg_pos, len, _elems) {}
virtual num_sequence *clone() const { return new Fibonacci(*this); }
protected:
virtual void gen_elems(int pos) const;
static vector<unsigned int> _elems;
};
class Pell : public num_sequence
{
public:
Pell(int beg_pos = 1, int len = 1) : num_sequence(beg_pos, len, _elems) {}
virtual num_sequence *clone() const { return new Pell(*this); }
protected:
virtual void gen_elems(int pos) const;
static vector<unsigned int> _elems;
};
class Lucas : public num_sequence
{
public:
Lucas(int beg_pos = 1, int len = 1) : num_sequence(beg_pos, len, _elems) {}
virtual num_sequence *clone() const { return new Lucas(*this); }
protected:
virtual void gen_elems(int pos) const;
static vector<unsigned int> _elems;
};
class Triangular : public num_sequence
{
public:
Triangular(int beg_pos = 1, int len = 1) : num_sequence(beg_pos, len, _elems) {}
virtual num_sequence *clone() const { return new Triangular(*this); }
protected:
virtual void gen_elems(int pos) const;
static vector<unsigned int> _elems;
};
class Square : public num_sequence
{
public:
Square(int beg_pos = 1, int len = 1) : num_sequence(beg_pos, len, _elems) {}
virtual num_sequence *clone() const { return new Square(*this); }
protected:
virtual void gen_elems(int pos) const;
static vector<unsigned int> _elems;
};
class Pentagonal : public num_sequence
{
public:
Pentagonal(int beg_pos = 1, int len = 1) : num_sequence(beg_pos, len, _elems) {}
virtual num_sequence *clone() const { return new Pentagonal(*this); }
protected:
virtual void gen_elems(int pos) const;
static vector<unsigned int> _elems;
};
inline void display(ostream &os, const num_sequence &ns, int pos, int elem_val)
{
os << "The element at position " << pos;
os << " for the ";
os << ns.what_am_i();
os << " sequence is " << elem_val << endl;
return;
}
inline void display(ostream &os, const num_sequence &ns, int pos)
{
os << "The element at position ";
os << pos << " for the ";
os << ns.what_am_i() << " sequence is ";
os << ns.elem(pos) << endl;
return;
}
void prog1()
{
const int pos = 8;
cout << "Test1:" << endl;
Fibonacci fib;
display(cout, fib, pos);
Pell pell;
display(cout, pell, pos);
Lucas lucas;
display(cout, lucas, pos);
Triangular trian;
display(cout, trian, pos);
Square square;
display(cout, square, pos);
Pentagonal penta;
display(cout, penta, pos);
return;
}
void prog2()
{
Fibonacci fib(1, 8);
Pell pell(4, 6);
Lucas lucas(7, 10);
Triangular trian(1, 12);
Square square(6, 6);
Pentagonal penta(1, 8);
cout << "\nTest2:" << endl;
cout << "fib:\t" << fib << '\n';
cout << "pell:\t" << pell << '\n';
cout << "lucas:\t" << lucas << '\n';
cout << "trian:\t" << trian << '\n';
cout << "square:\t" << square << '\n';
cout << "penta:\t" << penta << endl;
return;
};
void prog3()
{
num_sequence *pns = nullptr;
int elem_val = 0;
const int pos = 8;
const int find_pos = 14;
int elem_values[6] = {377, 80782, 843, 105, 196, 287};
cout << "\nTest3:" << endl;
for (int ix = 0; ix < 6; ++ix)
{
switch (ix)
{
case 0:
pns = new Fibonacci(1, 8);
break;
case 1:
delete pns;
pns = new Pell(1, 8);
break;
case 2:
delete pns;
pns = new Lucas(1, 8);
break;
case 3:
delete pns;
pns = new Triangular(1, 8);
break;
case 4:
delete pns;
pns = new Square(1, 8);
break;
case 5:
delete pns;
pns = new Pentagonal(1, 8);
break;
default:
delete pns;
return;
}
elem_val = pns->elem(pos);
display(cout, *pns, pos, elem_val);
cout << pns->what_am_i() << " : ( ";
cout << pns->beg_pos() << ", ";
cout << pns->length() << " ) : ";
cout << *pns << endl;
pns->set_length(12);
cout << *pns << endl;
cout << "num_sequence::is_elem() ";
if (!pns->is_elem(elem_val) || pns->is_elem(2 * elem_val))
{
cout << "failed\n";
}
else
{
cout << "ok!\n";
}
cout << "copy constructor and equality ";
num_sequence *pns2 = pns->clone();
if (*pns != *pns2)
{
cout << "failed!\n";
}
else
{
cout << "ok!\n";
}
pns2->set_position(5);
int posit = pns->pos_elem(elem_val);
if (pns->pos_elem(elem_val) != pos)
{
cout << "pos_elem() failed : " << posit << "\n";
}
else
{
cout << "pos_elem() -- ok: " << posit << "\n";
}
posit = pns->pos_elem(elem_values[ix]);
if (posit != find_pos)
{
cout << "pos_elem(): not found but grown: failed: ";
cout << posit << " correct: " << find_pos << "\n";
}
else
{
cout << "pos_elem(): not found but grown: ok\n";
}
cout << "about to print final ns vector: \n";
pns->print();
cout << endl;
cout << "ns2: should begin at position 5:\n";
cout << *pns2 << endl;
}
return;
}
vector<unsigned int> Fibonacci::_elems;
vector<unsigned int> Pell::_elems;
vector<unsigned int> Lucas::_elems;
vector<unsigned int> Triangular::_elems;
vector<unsigned int> Square::_elems;
vector<unsigned int> Pentagonal::_elems;
void Fibonacci::gen_elems(int pos) const
{
if (pos <= 0 || pos > max_elems())
{
return;
}
if (_elems.empty())
{
_elems.push_back(1);
_elems.push_back(1);
}
if (_elems.size() <= pos)
{
int ix = _elems.size();
int n_2 = _elems[ix - 2];
int n_1 = _elems[ix - 1];
for (int elem; ix <= pos; ++ix)
{
elem = n_2 + n_1;
_elems.push_back(elem);
n_2 = n_1;
n_1 = elem;
}
}
return;
}
void Pell::gen_elems(int pos) const
{
if (pos <= 0 || pos > max_elems())
{
return;
}
if (_elems.empty())
{
_elems.push_back(1);
_elems.push_back(2);
}
if (_elems.size() <= pos)
{
int ix = _elems.size();
int n_2 = _elems[ix - 2];
int n_1 = _elems[ix - 1];
for (int elem; ix <= pos; ++ix)
{
elem = n_2 + 2 * n_1;
_elems.push_back(elem);
n_2 = n_1;
n_1 = elem;
}
}
return;
}
void Lucas::gen_elems(int pos) const
{
if (pos <= 0 || pos > max_elems())
{
return;
}
if (_elems.empty())
{
_elems.push_back(1);
_elems.push_back(3);
}
if (_elems.size() <= pos)
{
int ix = _elems.size();
int n_2 = _elems[ix - 2];
int n_1 = _elems[ix - 1];
for (int elem; ix <= pos; ++ix)
{
elem = n_2 + n_1;
_elems.push_back(elem);
n_2 = n_1;
n_1 = elem;
}
}
return;
}
void Triangular::gen_elems(int pos) const
{
if (pos <= 0 || pos > max_elems())
{
return;
}
if (_elems.size() <= pos)
{
int end_pos = pos + 1;
for (int ix = _elems.size() + 1; ix <= end_pos; ++ix)
{
_elems.push_back(ix * (ix + 1) / 2);
}
}
return;
}
void Square::gen_elems(int pos) const
{
if (pos <= 0 || pos > max_elems())
{
return;
}
if (_elems.size() <= pos)
{
int end_pos = pos + 1;
for (int ix = _elems.size() + 1; ix <= end_pos; ++ix)
{
_elems.push_back(ix * ix);
}
}
return;
}
void Pentagonal::gen_elems(int pos) const
{
if (pos <= 0 || pos > max_elems())
{
return;
}
if (_elems.size() <= pos)
{
int end_pos = pos + 1;
for (int ix = _elems.size() + 1; ix <= end_pos; ++ix)
{
_elems.push_back(ix * (3 * ix - 1) / 2);
}
}
return;
}
int main()
{
prog1();
prog2();
prog3();
return 0;
}
//-----------------------------------------------------
//Practise5.1.cpp
#include <iostream>
#include <vector>
#include <string>
using namespace std;
typedef string elemtype;
class Stack
{
public:
virtual ~Stack() {}
virtual bool pop(elemtype &elem) = 0;
virtual bool push(const elemtype &elem) = 0;
virtual bool peek(int index, elemtype &elem) = 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 &os = cout) const = 0;
};
ostream &operator<<(ostream &os, const Stack &rhs)
{
rhs.print();
return os;
}
class LIFO_Stack : public Stack
{
private:
vector<elemtype> st;
public:
LIFO_Stack(int capacity = 0) { st.reserve(capacity); }
virtual ~LIFO_Stack() {}
bool pop(elemtype &elem)
{
if (empty())
{
return false;
}
elem = st.back();
st.pop_back();
return true;
}
bool push(const elemtype &elem)
{
if (full())
{
return false;
}
st.push_back(elem);
return true;
}
bool peek(int index, elemtype &elem) { return false; }
int top() const { return st.size(); }
int size() const { return st.size(); }
bool empty() const { return 0 == st.size(); }
bool full() const { return st.size() >= st.max_size(); }
void print(ostream &os) const
{
for (vector<elemtype>::const_reverse_iterator it = st.rbegin(); it != st.rend(); ++it)
{
cout << *it << ' ';
}
return;
}
};
class Peekback_Stack : public Stack
{
private:
vector<elemtype> st;
public:
Peekback_Stack(int capacity = 0) { st.reserve(capacity); }
virtual ~Peekback_Stack() {}
bool pop(elemtype &elem)
{
if (empty())
{
return false;
}
elem = st.back();
st.pop_back();
return true;
}
bool push(const elemtype &elem)
{
if (full())
{
return false;
}
st.push_back(elem);
return true;
}
bool peek(int index, elemtype &elem);
int top() const { return st.size(); }
int size() const { return st.size(); }
bool empty() const { return 0 == st.size(); }
bool full() const { return st.size() >= st.max_size(); }
void print(ostream &os) const
{
for (vector<elemtype>::const_reverse_iterator it = st.rbegin(); it != st.rend(); ++it)
{
cout << *it << ' ';
}
return;
}
};
bool Peekback_Stack::peek(int index, elemtype &elem)
{
if (empty() || index < 0 || index >= st.size())
{
return false;
}
elem = st[index];
return true;
}
void test(Stack &st, int index)
{
string s;
if (st.peek(index, s))
{
cout << "peek: " << s;
}
else
{
cout << "peek failed!";
}
cout << endl;
return;
}
int main()
{
string str;
LIFO_Stack st;
cout << "Please enter a series of strings." << endl;
while (cin >> str && !st.full())
{
st.push(str);
}
cout << "About to call peek() with LIFO_Stack" << endl;
test(st, st.top() - 1);
cout << st << endl;
Peekback_Stack pst;
while (!st.empty())
{
string t;
if (st.pop(t))
{
pst.push(t);
}
}
cout << "\nAbout to call peek() with Peekback_Stack" << endl;
test(pst, pst.top() - 1);
cout << pst;
return 0;
}
//-----------------------------------------------------
//Practise5.2.cpp
#include <iostream>
#include <vector>
#include <string>
using namespace std;
typedef string elemtype;
class Stack
{
protected:
vector<elemtype> st;
public:
Stack(int capacity = 0) { st.reserve(capacity); }
virtual ~Stack() {}
bool pop(elemtype &elem)
{
if (empty())
{
return false;
}
elem = st.back();
st.pop_back();
return true;
}
bool push(const elemtype &elem)
{
if (full())
{
return false;
}
st.push_back(elem);
return true;
}
virtual bool peek(int index, elemtype &elem) { return false; }
int top() const { return st.size(); }
int size() const { return st.size(); }
bool empty() const { return 0 == st.size(); }
bool full() const { return st.size() >= st.max_size(); }
void print(ostream &os = cout) const
{
for (vector<elemtype>::const_reverse_iterator it = st.rbegin(); it != st.rend(); ++it)
{
cout << *it << ' ';
}
return;
}
};
ostream &operator<<(ostream &os, const Stack &rhs)
{
rhs.print();
return os;
}
class Peekback_Stack : public Stack
{
private:
vector<elemtype> st;
public:
Peekback_Stack(int capacity = 0) : Stack(capacity) {}
virtual ~Peekback_Stack() {}
virtual bool peek(int index, elemtype &elem);
};
bool Peekback_Stack::peek(int index, elemtype &elem)
{
if (empty() || index < 0 || index >= st.size())
{
return false;
}
elem = st[index];
return true;
}
void test(Stack &st, int index)
{
string s;
if (st.peek(index, s))
{
cout << "peek: " << s;
}
else
{
cout << "peek failed!";
}
cout << endl;
return;
}
int main()
{
Stack st;
string str;
Peekback_Stack pst;
cout << "Please enter a series of strings." << endl;
while (cin >> str && !st.full())
{
st.push(str);
}
cout << "About to call peek() with Stack" << endl;
test(st, st.top() - 1);
cout << st << endl;
while (!st.empty())
{
string t;
if (st.pop(t))
{
pst.push(t);
}
}
cout << "\nAbout to call peek() with Peekback_Stack" << endl;
test(pst, pst.top() - 1);
cout << pst;
return 0;
}
//-----------------------------------------------------
//--------------------------------------------2021年7月25日 ----------------------------------------------------
|