1 面向对象中两个重要概念
1 派生类
派生类:利用继承机制,新的类可以从已有的类中派生。那些用于派生的类称为这些特别派生出的类的“基类”。
2 虚函数
虚函数可实现多态性 派生类只调用自己的函数。
下面来看示例:
class Stack{
public:
virtual void push(char c) = 0; // 虚函数 基类只有定义
virtual char pop() = 0;
};
基类只有原型的定义
class ArrayStack:public Stack{
char* p;
int maxSize;
int top;
public:
ArrayStack(int s)
{
top = 0;
maxSize = s;
//*****found*****1
//p=_____;
p = new char[s]; //为p申请s个char型空间
}
~ArrayStack()
{
//*****found*****2
//_____;
delete [] p; //释放指针 new的空间需要释放
}
void push(char c)
{
if(top == maxSize){
cerr<<"Overflow!\n";
return;
}
//*****found*****3
//_____;
p[top] = c; //top为栈顶元素下标 添加的数据放到栈顶
top++;
}
char pop()
{
if(top == 0){
cerr<<"Underflow!\n";
return '\0';
}
top--;
//*****found*****4
//_____;
return p[top];
}
};
派生类中,对push函数做了具体的实现。
那么 ,到底何时调用基类的虚函数,何时调用派生类的虚函数呢。这里可以做一个实验。
#include <QtCore/QCoreApplication>
#include <iostream>
using namespace std;
// 类定义
class A
{
public:
virtual void print()
{
cout<<"这是基类A的print函数"<<endl;
}
};
class B : public A
{
public:
virtual void print()
{
cout<<"这是派生类B的print函数"<<endl;
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 定义2个对象
A aa;
B bb;
// 赋值指针
A *p1 = &aa;
A *p2 = &bb;
// 调用成员函数
p1->print();
p2->print();
return a.exec();
}
简单讲解一下,A是基类,B是派生类,print是虚函数。
先定义A类的实例aa,在定义B类的实例bb,然后定义基类指针p1和p2,再将aa和bb的地址赋予指针。这时候,p1输出的是A的函数,而p2输出的是B的函数。
虚函数的理论还是蛮绕的,就不误导观众了,这里最好找教材好好复习一下,这是个重要的知识点。
2 字符数组的开辟和销毁
字符数组是本次学习的重点。
先来看下代码:
#include <QtCore/QCoreApplication>
#include <iostream>
using namespace std;
// 类定义
class Stack{
public:
virtual void push(char c) = 0; // 虚函数可实现多态性 派生类只调用自己的函数
virtual char pop() = 0;
};
class ArrayStack:public Stack{
char* p;
int maxSize;
int top;
public:
ArrayStack(int s){
top = 0;
maxSize = s;
p = new char[s]; //为p申请s个char型空间
}
~ArrayStack(){
delete [] p; //释放指针 new的空间需要释放
}
void push(char c){
if(top == maxSize){
cerr<<"Overflow!\n";
return;
}
p[top] = c; //top为栈顶元素下标 添加的数据放到栈顶
top++;
}
char pop(){
if(top == 0){
cerr<<"Underflow!\n";
return '\0';
}
top--;
return p[top];
}
};
void f(Stack& sRef){
char ch[] = {'a','b','c'};
cout<<ch[0]<<","<<ch[1]<<","<<ch[2]<<endl;
sRef.push(ch[0]); sRef.push(ch[1]); sRef.push(ch[2]);
cout<<sRef.pop()<<",";
cout<<sRef.pop()<<",";
cout<<sRef.pop()<<endl;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
ArrayStack as(10);
f(as);
return a.exec();
}
输出结果为
先来分析下类结构:
基类Stack | 派生类ArrayStack |
char* p; int maxSize; int top; | |
virtual void push(char c) = 0; virtual char pop() = 0; | ArrayStack(int s); ~ArrayStack(); void push(char c); char pop(); |
基类实现了2个虚函数,1个是进栈push,1个是出栈pop。都是空的。
派生类实现了4个函数,分别是构造函数,析构函数,进栈push和出栈pop。
下面来看下main函数的逻辑
- 声明ArrayStack的对象as(10),这里要调用构造函数。
- 构造函数里,令top为0,maxSize为10,p开辟长度为10的字符数组变量
- 回到main,执行f函数,其形参是as。这里注意函数里是Stack类型,是基类。
- 在f函数里,as被传进来。声明了ch字符数组,其3个元素为a,b,c。首先cout这3个元素。控制台显示a,b,c。
- 对as执行push操作,进入push环节。这里也只能执行派生类的函数了。
- ch[0]是a,a进入push函数后,先做判断top == maxSize,这里目前是0==10所以否掉,执行p[top] = c;就是p[0]='a'。随后top自加变为1。
- 跳出push后,又来第2个push,变量是ch[1]就是b。继续。1==10不成立,所以p[1]='b' top=2。
- 再次push,这里判断2==3不成立,所以p[2]='c',top=3。
- 整理一下3次push的操作,ArrayStack对象as的p变量的前3个元素依次被赋予a,b,c三个字符。后面空着的8个元素还是空的。
- 下面执行pop操作。进入pop后,top=3,3==0不成立,跳过if,top变2,p[2]被返回,就是c。之后cout就是打印c
- 继续pop,top=2,2==0不成立,跳过if,top变1,p[1]=b被返回cout打印。
- 继续pop,top=1,1==0不成立,跳过if,top=0,p[0]=a被返回打印。
- f函数被执行完毕。返回main函数
- 执行完毕。程序退出,开始执行析构函数。
- 销毁p空间。
分析上述过程,p数组其实占用内存空间是固定的10个。pop后,也没有销毁。只有在最后析构函数中才被销毁。
看来 使用char存储字符不是很方便,比如要执行字符串拼接等操作,可能还是需要string类型更好。