什么是纯虚函数?
-
纯虚函数也是虚函数(特殊的虚函数)
-
只是纯虚函数是没有函数体的
-
本质就是重写过程→用了别人的框架,别人把具体功能实现了,想要改变某些属性就需要重写
-
纯虚函数一般写为公有属性(要实现多态),如果只在类中调用,可以写为保护属性
//原来的虚函数不写函数体直接=0就是纯虚函数 在类中函数这样写
virtual void print()= 0;
用到别人的框架需要重写别人的方法:mfc中创建窗口的过程,父类中已经写好了,但是创建的窗口是默认窗口大小,作为用户可能不需要默认大小,想修改宽高--->提供了一个纯虚函数Instance用来初始化数据,重写这个函数,在这个函数中设置窗口的属性
什么是抽象类?
具有至少一个纯虚函数的类,叫做抽象类
-
抽象类不能构建对象
-
抽象类可以构建对象指针
-
抽象类不能在[类中声明,在类外实现]
抽象类不能在类中声明在类外实现
//抽象类
class Parent
{
public:
virtual void print();
protected:
};
void Parent::print()=0; //报错:纯虚函数不能在类中声明,类外实现
//不能在成员函数"Parent::print"的类外部重新声明该函数
抽象类不能构建对象,可以构建对象指针
//抽象类
class Parent
{
public:
virtual void print()= 0; //纯虚函数 无函数体
protected:
};
void testAbstract()
{
//Parent object; 报错:抽象类不能构建对象
Parent* parent = nullptr; //但是能构建指针
}
模拟一个栈的实现过程
-
入栈、出栈
-
获取栈元素
-
判断栈是否为空
-
获取栈的大小
纯虚函数就是做ADT(abstract data type 抽象数据类型) 过程
做架构过程,把项目搭建出来,测试代码写好了,剩下实现功能的代码让别人去实现
//stack 栈
class stack
{
public:
//抽象一个父类,已经知道栈的基本操作,把所有函数声明为抽象数据类型,父类中所有的属性描述好
virtual void push(int data) = 0;
virtual void pop() = 0;
virtual int top() const = 0;
virtual bool empty() const = 0;
virtual int size() const = 0;
};
class arrayStack :public stack //数组栈---用数组实现一个栈
{
public:
void push(int data) //入栈---入一个整数
{
}
void pop() //出栈
{
}
int top() const //获取栈最上面的元素
{
return 1;
}
bool empty() const //判断是否为空
{
return true;
}
int size() const //获取大小
{
return 1;
}
//可以增加别的函数---但是纯虚函数必须要重写
//可以增加别的数据成员
protected:
int* array; //数组栈---准备数组的指针
};
struct Node //节点---用链表实现,需要结构体
{
int data;
Node* next;
};
//不管做什么栈,重写抽象类即可
class listStack :public stack //链式栈---用链表实现一个栈
{
public:
void push(int data)
{
}
void pop()
{
}
int top() const
{
return 1;
}
bool empty() const
{
return true;
}
int size() const
{
return 1;
}
protected:
Node* headNode; //表头
};
void testStack(stack* pStack) //测试代码---传入抽象类的指针
{
pStack->push(1); //入栈,入1
while (!pStack->empty()) //判断栈是否为空
{
cout << pStack->top(); //如果栈不为空,获取栈顶元素,不断出栈
pStack->pop();
}
}
int main()
{
//无论什么方式实现,都可以调用这个测试代码 用数组|链表调用,测试代码是一样的,统一接口
testStack(new arrayStack);
testStack(new listStack);
return 0;
}
小知识:
-
子类想要创建对象,必须重写父类的纯虚函数
-
ADT:具有强迫性,所有子类重写函数必须和父类的一模一样(函数体可以不一样),如果不一样会报错
-
如果子类如果没有实现父类的方法,还是抽象类,不能构建对象,我们只能去实现一下这些功能,让它可以构建对象
-
可以增加别的函数&数据成员,但是父类中的抽象必须要重写
-
ADT就是虚函数和多态的使用,只是用的是纯虚函数而已
-
纯虚函数如果没有被重写,无论继承多少次都是纯虚函数,虚函数无论被继承多少次都是虚函数
无论被继承多少次,如果没有实现纯虚函数,这个类永远都是抽象类,一般情况下,抽象类只被继承一次就重写,因为一个类被继承多次后可能忘记哪个函数是纯虚函数
class A
{
public:
virtual void print() = 0; //纯虚函数
protected:
};
class B :public A //A类继承产生B类,B类还是抽象类
{
public:
};
class C :public B //B类再产生一个类,C类也是抽象类
{
public:
};
void Abtract()
{
//B b; 报错:B|C类没有重写不能构建对象
//C c; C也是抽象类
}
重写B类后,C类能构建对象
class A
{
public:
virtual void print() = 0; //纯虚函数
protected:
};
class B :public A
{
public:
void print()
{
cout << "B" << endl;
}
};
class C :public B
{
public:
};
void Abtract()
{
//B类重写后能构建对象
B b;
C c;
}
纯虚函数也是虚函数的一种(虚函数无论被继承多少次都是虚函数)
在实现多态的时候,不需要virtual修饰
class A
{
public:
virtual void print() = 0; //纯虚函数
protected:
};
class B :public A
{
public:
void print()
{
cout << "B" << endl;
}
};
class C :public B
{
public:
void print()
{
cout << "C" << endl;
}
};
void Abtract()
{
C c;
B* pb = new C; //子类对象初始化父类指针
pb->print(); //虚函数无论被继承多少次一直都是虚函数,有无virtual,都存在一个虚函数
}
int main()
{
//下面实现多态,不需要virtual
Abtract(); /*输出:c 子类对象初始化父类指针没有virutal看指针类型,有
virtual看对象类型,这里是看对象类型:C的类型*/
return 0;
}