概述
目录
- 总结
- 习题
- 5.1
- 5.2
- 报错信息学习(新的一批,包括代码顺序位置问题)
- 关于reverse_iterator(逆向迭代器)的学习
- 关于奇怪的代码学习
总结
这章开始介绍了面向对象编程概念,最主要特质:继承和多态
继承我们知道了父类和子类,即基类和派生类,这两个类之间的关系叫继承体系
多态:动态绑定
然后介绍了继承体系,LibMat类,Book类和AudioBook类的三层类继承体系
介绍不带继承的多态(类之间的继承关系不大)
如何定义抽象基类,三要素(三步法)
如何定义派生类,很多要求
定义完了抽象基类和派生类,那么接下来就要对他们进行运用,
紧接着告诉你基类可以抽象,也可以具象化称为具象基类
然后说了一下具象基类的初始化析构复制这三个操作
派生类怎么定义虚函数包括虚函数的静态解析,很好体现了多态性
最后说明了RTTI(运行时鉴定机制),引入新名词
习题
5.1
实现一个两层栈类体系,基类纯抽象类,只提供最简单接口如下。两个派生类(LIFO_Stack和Peekback_Stack)自己具象化定义,Peekback_Stack类可以让用户在不更改stack元素的前提下,访问任何一个元素。
//为了遍历输出vector容器所有存储元素成栈的存储元素形式,我采用const_reverse_iterator,这种泛型指针可以以逆向方式,即由容器尾端
//(容器最末尾元素位置的下个位置)到前端(容器第一个元素的位置)遍历整个vector容器
#include<iostream>
#include<string>
#include<vector>
using namespace std;//它最大,typedef不能放在它前面!否则编译器报错
//error: 'string' does not name a type|
//error: 'elemType' has not been declared|
//(节选部分相同错误)
typedef string elemType;
//嵌套类型定义,给中间这个类型关键字(类型名)
//起个别的名字),这里只是为了灵活,这样栈里可以都存比如string型
//int型或者double型元素
class Stack{
public:
virtual ~Stack(){}
virtual bool pop(elemType&) =0;
virtual bool push(const elemType&) =0;
virtual int size() const =0;
//基类成员函数和派生类同名成员虚函数声明/定义的函数原型不一致,会报错
//error: conflicting return type specified for 'virtual int LIFO_Stack::size() const'|
//error:
overriding 'virtual bool Stack::size() const'|
virtual bool empty()const =0;
virtual bool full()const =0;
virtual bool peek(int index,elemType&) =0;
virtual void print(ostream& =cout) const =0;//成员函数声明时
//提供默认参数值
};//只提供接口的纯抽象类Stack
ostream& operator<<(ostream &os,const Stack &rhs)
{
rhs.print();
return os;
}
//重载运算符<<,意思就是打印<<左边的类对象的内容
class LIFO_Stack : public Stack{//Stack的派生类LIFO_Stack(栈实体)
public:
LIFO_Stack(int capacity =0):_top(0)
{
_stack.reserve(capacity);
//reserve不要拼写成reverse,否则报错:
//error: 'class std::vector<std::__cxx11::basic_string<char> >' has no member named 'reverse'|
}
//LIFO_Stack派生类且采用成员列表初始化的类构造函数
//因为栈的先进后出的特性,所以_stack私有成员vector容器(正序存储元素)
//需要把容器内元素逆序,最后一个上第一个(栈顶),第一个上最后一个(栈底)
//倒数第二个上第二个,第二个上倒数第二个,就这样反转vector容器
//首先创建了栈类对象,这个栈肯定是空栈,所以栈顶指针指向0代表空栈
inline int size() const{return _stack.size();}
//查询栈的大小
//当我们在派生类中,为了覆盖基类的某个虚函数而进行同名函数的声明时,
//可以不加关键字virtual
inline bool empty() const {return !_top;}
//空了就输出true,所以!_top;
inline bool full() const {return size()>=_stack.max_size();}
//max_size()泛型算法,功能是算容器的最大长度
//栈满返回true
inline int top()const{return _top;}//返回栈顶指针
//这里const拼写有误,导致报错:
//error: expected ';' at end of member declaration|
bool pop(elemType &elem)
{
if(empty())
{
return false;//栈空,无法删除栈顶元素
}
elem=_stack[--_top];//先栈顶指针下移,然后弹出栈顶元素
//即当前栈顶指针指向的元素
_stack.pop_back();//弹出栈顶元素
return true;
}
bool peek(int, elemType&){return false;}
//这段代码不能删,否则报错(调用的子类中没有实现基类的纯虚函数)
//error: cannot declare variable 'st' to be of abstract type 'LIFO_Stack'|
bool push(const elemType &elem)
{
if(full())
{
return false;//栈满就不可以再让元素入栈了
}
_stack.push_back(elem);//元素入栈(顶)
_top++;
return true;
}
void print(ostream&os)const成员函数声明时
//提供默认参数值,故函数定义时不再提供默认参数值
{
vector<elemType>::const_reverse_iterator rit=_stack.rbegin(),
rend=_stack.rend();
//我第一个栈的元素输出是正序输出,原因就在这里了,
//这里的一切都是为了从栈顶开始输出一直到栈底以符合栈的存储特点
//也是一种栈的遍历方式
//(有没有const_无所谓)const_reverse_iterator++,则对于正向容器来说是从尾向头遍历.
//begin 和 end 表示基础迭代器(容器的第一个元素的位置和尾元素下一个位置),
//rbegin和 rend分别表示由 begin 和 end 获得的反向迭代器(相当于begin和end调个)
os<<"nt";
while(rit!=rend)
{
os<<*rit++<<"nt";//这里因为是从右向左遍历vecto容器,所以++相当于左移泛型指针rit
}
os<<endl;
}//这函数功能就是输出栈的所有元素
private://派生类的私有成员,因为每个派生类都代表一个栈,所以自己的栈的存储形式不可以被一般程序或者其派生类直接访问。
vector<elemType>_stack;
int _top;
};
//Peekback_Stack可以让用户在不更改定义出的Peekback_Stack(栈)类类
//对象的元素的前提下,访问任何一个元素(这是和上面的LIFO_Stack派生类的一个区别)
class Peekback_Stack:public Stack{
public:
Peekback_Stack(int capacity=0)
:_top(0)
{
_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; }
bool pop( elemType &elem ){
if ( empty() )
return false;
elem = _stack[ --_top ];
_stack.pop_back();
return true;
}
bool push( const elemType &elem ){
if ( ! full() ){
_stack.push_back( elem );
++_top;
return true;
}
return false;
}
bool peek( int index, elemType &elem );
void print( ostream &os=cout ) const
{
vector<elemType>::const_reverse_iterator
rit = _stack.rbegin(),
rend = _stack.rend();
os << "nt";
while ( rit != rend )
os << *rit++ << "nt";//这里因为是从右向左遍历vecto容器,所以++相当于左移泛型指针rit
os << endl;
}
private:
vector<elemType>_stack;
int _top;
};
bool Peekback_Stack::peek(int index,elemType &elem)
{
if(empty())
{
return false;
}
if(index<0||index>=size())
{
return false;
}
elem=_stack[index];
return true;
//栈空和输入的索引值非法就返回false,否则返回true,证明找到了
//索引值对应的栈内的元素(栈是顺序存储的)
}
void peek(Stack &st,int index)
{
cout<<endl;
string t;
if(st.peek(index,t))//涉及到了基类指针和虚函数的知识。
{//运行时解析基类指针应该指向LIFO_Stack类的peek成员函数
//还是Peekback_Stack类的peek成员函数
cout<<"peek:"<<t;
}
else
{
cout<<"peek failed!";
}
cout<<endl;
//找到了要查询的栈的元素就输出找到的元素,没有找到就输出
//查找失败
}
int main()
{
LIFO_Stack st;
//error: cannot declare variable 'st' to be of abstract type 'LIFO_Stack'|报错点在这里,报错原因在下面(也可以定位到下面这行代码的实际地点去看
//报错原因)
//bool peek(int, elemType&){return false;}
//这段代码不能删,否则报错(调用的子类中没有实现基类的纯虚函数)
//error: cannot declare variable 'st' to be of abstract type 'LIFO_Stack'|
string str;//栈内要存放string型元素
cout<<"Please enter a series of string.n";
while(cin>>str&&!st.full())//ctrl+z停止输入(仅此办法)
{
st.push(str);//往栈里充元素(元素一个个压入栈内)
}
cout<<endl<<"About to call peek() with LIFO_Stack"<<endl;
peek(st,st.top()-1);
//要查询哪个栈类对象,以及索引值都传入peek这个一般函数中
cout<<st;//输出所有栈中元素
Peekback_Stack pst;
while(!st.empty())
{
string t;
if(st.pop(t))
{
pst.push(t);
}//从LIFO_Stack派生类类对象(栈)弹出来的元素去了Peekback_Stack派生类
//类对象(栈)里。(注意栈的先进后出后进先出特性,原先LIFO_Stack类对象st(栈)里存的元素遍历!顺序为time a upon once
//time先出st栈并去了Peekback_Stack类对象pst(栈),成为了pst栈的第一个元素(栈底元素),然后是a,成为了pst栈的第二个元素(次栈底元素)
//upon成了pst栈的次栈顶元素,once成了pst栈的栈顶元素,于是pst栈里存的元素遍历!顺序为once upon a time,懂了吗!
}
cout<<"About to call peek() with Peekback_Stack"<<endl;
peek(pst,pst.top()-1);
//要查询哪个栈类对象,以及索引值都传入peek这个一般函数中
cout<<pst;//输出所有栈中元素
}
//第一个查询索引值所代表的栈中的元素显示peek failed!因为我没写派生类LIFO_Stack的peek成员函数
//第一个查询索引值所代表的栈中的元素显示peek:once 因为我写了派生类Peekback_Stack的peek成员函数,而且索引值就是代表栈顶元素的位置。
//从而输出了peek:once
5.2
重新实现5.1的栈类体系,令基类Stack实现出各派生类共享的、与类型无关的所有成员。
#include<iostream>
#include<string>
#include<vector>
using namespace std;//它最大,typedef不能放在它前面!否则编译器报错
//error: 'string' does not name a type|
//error: 'elemType' has not been declared|
//(节选部分相同错误)
typedef string elemType;
//嵌套类型定义,给中间这个类型关键字(类型名)
//起个别的名字),这里只是为了灵活,这样栈里可以都存比如string型
//int型或者double型元素
class Stack{//Stack的派生类LIFO_Stack(栈实体)
public:
Stack(int capacity =0):_top(0)
{
_stack.reserve(capacity);
//reserve不要拼写成reverse,否则报错:
//error: 'class std::vector<std::__cxx11::basic_string<char> >' has no member named 'reverse'|
}
//LIFO_Stack派生类且采用成员列表初始化的类构造函数
//因为栈的先进后出的特性,所以_stack私有成员vector容器(正序存储元素)
//需要把容器内元素逆序,最后一个上第一个(栈顶),第一个上最后一个(栈底)
//倒数第二个上第二个,第二个上倒数第二个,就这样反转vector容器
//首先创建了栈类对象,这个栈肯定是空栈,所以栈顶指针指向0代表空栈
virtual ~Stack(){}//类析构函数放在类构造函数后面最好。
inline int size() const{return _stack.size();}
//查询栈的大小
//当我们在派生类中,为了覆盖基类的某个虚函数而进行同名函数的声明时,
//可以不加关键字virtual
inline bool empty() const {return !_top;}
//空了就输出true,所以!_top;
inline bool full() const {return size()>=_stack.max_size();}
//max_size()泛型算法,功能是算容器的最大长度
//栈满返回true
inline int top()const{return _top;}//返回栈顶指针
//这里const拼写有误,导致报错:
//error: expected ';' at end of member declaration|
bool pop(elemType &elem)
{
if(empty())
{
return false;//栈空,无法删除栈顶元素
}
elem=_stack[--_top];//先栈顶指针下移,然后弹出栈顶元素
//即当前栈顶指针指向的元素
_stack.pop_back();//弹出栈顶元素
return true;
}
virtual bool peek(int, elemType&){return false;}
//这段代码不能删,否则报错(调用的子类中没有实现基类的纯虚函数)
//error: cannot declare variable 'st' to be of abstract type 'LIFO_Stack'|
bool push(const elemType &elem)
{
if(full())
{
return false;//栈满就不可以再让元素入栈了
}
_stack.push_back(elem);//元素入栈(顶)
_top++;
return true;
}
void print(ostream &os=cout)const成员函数声明时
//提供默认参数值,故函数定义时不再提供默认参数值
{
vector<elemType>::const_reverse_iterator rit=_stack.rbegin(),
rend=_stack.rend();
//我第一个栈的元素输出是正序输出,原因就在这里了,
//这里的一切都是为了从栈顶开始输出一直到栈底以符合栈的存储特点
//也是一种栈的遍历方式
//(有没有const_无所谓)const_reverse_iterator++,则对于正向容器来说是从尾向头遍历.
//begin 和 end 表示基础迭代器(容器的第一个元素的位置和尾元素下一个位置),
//rbegin和 rend分别表示由 begin 和 end 获得的反向迭代器(相当于begin和end调个)
os<<"nt";
while(rit!=rend)
{
os<<*rit++<<"nt";//这里因为是从右向左遍历vecto容器,所以++相当于左移泛型指针rit
}
os<<endl;
}//这函数功能就是输出栈的所有元素
protected://这里设计为protected是为了让Stack基类的派生类访问基类的vector容器和栈顶“指针”(_top)
vector<elemType>_stack;
int _top;
};
ostream& operator<<(ostream &os,const Stack &rhs)//先定义Class类,再定义<<重载运算符函数,否则报错说没有Stack这个类型!
{
rhs.print();
return os;
}
//重载运算符<<,意思就是打印<<左边的类对象的内容
//基类从抽象基类变为具象基类
//Peekback_Stack可以让用户在不更改定义出的Peekback_Stack(栈)类类
//对象的元素的前提下,访问任何一个元素(这是和上面的LIFO_Stack派生类的一个区别)
class Peekback_Stack:public Stack{
public:
Peekback_Stack(int capacity=0):Stack(capacity){}
//具象基类里面对于栈相关的函数和对象应有尽有,这里只写个构造函数就可以了
virtual bool peek(int index,elemType &elem);//此处virtual可以不加,但是咱们表明从基类继承而来的这个peek()同名成员函数是虚函数!!!
//这里扯到了继承和虚函数的知识点,记住!写笔记!
};
bool Peekback_Stack::peek(int index,elemType &elem)
{
if(empty())
{
return false;
}
if(index<0||index>=size())
{
return false;
}
elem=_stack[index];
return true;
//栈空和输入的索引值非法就返回false,否则返回true,证明找到了
//索引值对应的栈内的元素(栈是顺序存储的)
}
void peek(Stack &st,int index)
{
cout<<endl;
string t;
if(st.peek(index,t))//涉及到了基类指针和虚函数的知识。
{//运行时解析基类指针应该指向LIFO_Stack类的peek成员函数
//还是Peekback_Stack类的peek成员函数
cout<<"peek:"<<t;
}
else
{
cout<<"peek failed!";
}
cout<<endl;
//找到了要查询的栈的元素就输出找到的元素,没有找到就输出
//查找失败
}//移植51exercise的一些成员函数和非成员函数的时候注意不要少移植。
int main()
{
Stack st;
//error: cannot declare variable 'st' to be of abstract type 'LIFO_Stack'|报错点在这里,报错原因在下面(也可以定位到下面这行代码的实际地点去看
//报错原因)
//bool peek(int, elemType&){return false;}
//这段代码不能删,否则报错(调用的子类中没有实现基类的纯虚函数)
//error: cannot declare variable 'st' to be of abstract type 'LIFO_Stack'|
string str;//栈内要存放string型元素
cout<<"Please enter a series of string.n";
while(cin>>str&&!st.full())//ctrl+z停止输入(仅此办法)
{
st.push(str);//往栈里充元素(元素一个个压入栈内)
}
cout<<endl<<"About to call peek() with LIFO_Stack"<<endl;
peek(st,st.top()-1);
//要查询哪个栈类对象,以及索引值都传入peek这个一般函数中
cout<<st;//输出所有栈中元素
Peekback_Stack pst;
while(!st.empty())
{
string t;
if(st.pop(t))
{
pst.push(t);
}//从LIFO_Stack派生类类对象(栈)弹出来的元素去了Peekback_Stack派生类
//类对象(栈)里。(注意栈的先进后出后进先出特性,原先LIFO_Stack类对象st(栈)里存的元素遍历!顺序为time a upon once
//time先出st栈并去了Peekback_Stack类对象pst(栈),成为了pst栈的第一个元素(栈底元素),然后是a,成为了pst栈的第二个元素(次栈底元素)
//upon成了pst栈的次栈顶元素,once成了pst栈的栈顶元素,于是pst栈里存的元素遍历!顺序为once upon a time,懂了吗!
}
cout<<"About to call peek() with Peekback_Stack"<<endl;
peek(pst,pst.top()-1);
//要查询哪个栈类对象,以及索引值都传入peek这个一般函数中
cout<<pst;//输出所有栈中元素
}
//第一个查询索引值所代表的栈中的元素显示peek failed!因为我没写派生类LIFO_Stack的peek成员函数
//第一个查询索引值所代表的栈中的元素显示peek:once 因为我写了派生类Peekback_Stack的peek成员函数,而且索引值就是代表栈顶元素的位置。
//从而输出了peek:once
报错信息学习(新的一批,包括代码顺序位置问题)
-
using namespace std;//它最大,typedef不能放在它前面!否则编译器报错 //error: ‘string’
does not name a type| //error: ‘elemType’ has not been declared|
//(节选部分相同错误) -
基类成员函数和派生类同名成员虚函数声明/定义的函数原型不一致,会报错
//error: conflicting return type specified for ‘virtual int LIFO_Stack::size() const’|
//error: overriding ‘virtual bool Stack::size() const’| -
reserve(容器的泛型算法)不要拼写成reverse,否则报错:
//error: ‘class std::vector<std::__cxx11::basic_string >’ has no member named ‘reverse’| -
这里const拼写有误,导致报错:
//error: expected ‘;’ at end of member declaration| -
bool peek(int, elemType&){return false;}(雾)
//这段代码不能删,否则报错(调用的子类中没有实现基类的纯虚函数)
//error: cannot declare variable ‘st’ to be of abstract type ‘LIFO_Stack’| -
error: cannot declare variable ‘st’ to be of abstract type ‘LIFO_Stack’|报错点在这里,报错原因在下面(也可以定位到下面这行代码的实际地点去看
//报错原因)
//bool peek(int, elemType&){return false;}
//这段代码不能删,否则报错(调用的子类中没有实现基类的纯虚函数)
//error: cannot declare variable ‘st’ to be of abstract type ‘LIFO_Stack’|
关于reverse_iterator(逆向迭代器)的学习
(有没有const_无所谓)const_reverse_iterator++,则对于正向容器来说是从尾向头遍历.
begin 和 end 表示基础迭代器(容器的第一个元素的位置和尾元素下一个位置),
rbegin和 rend分别表示由 begin 和 end 获得的反向迭代器(相当于begin和end调个)
用法举例:
void print(ostream &os=cout)const
{
vector<elemType>::const_reverse_iterator rit=_stack.rbegin(),
rend=_stack.rend();
os<<"nt";
while(rit!=rend)
{
os<<*rit++<<"nt";
}
os<<endl;
}
这样便可以从容器尾遍历到容器头了,rbegin(rit)是代表容器尾,rend代表容器头。
关于奇怪的代码学习
virtual bool peek(int, elemType&){return false;}不需要用到参数可以不写,只要接受到相应参数。调用就正确(颠覆认知)
这个函数是派生类的成员函数,在基类里该同名成员函数是纯虚函数,
看来这个派生类为了不继承纯虚函数从而成为了抽象派生类导致无法定义类对象而写了个这么样的函数
这种语法一般用来检查类型的
一般没人写这种代码。
最后
以上就是高挑百合为你收集整理的EssentialC++第五章总结+课后习题+踩雷清单总结习题的全部内容,希望文章能够帮你解决EssentialC++第五章总结+课后习题+踩雷清单总结习题所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复