(一)公有虚函数
子类通过覆盖公有虚函数的方式实现多态是最常见的情况。指向子类对象的基类指针调用被子类覆盖的函数,实际上,调用的是子类的函数。以下代码:
class Base
{
public:
virtual void foo()
{
cout<<"Base::foo"<<endl;
}
};
class Derive:public Base
{
void foo()
{
cout<<"Derive::foo"<<endl;
}
};
编写了两个类,其中Derive类公有继承Base类,覆盖Base类的虚函数foo(),在main函数中,编写测试代码:
Base *pb = new Derive();
pb->foo();
毫无疑问,在上面的代码中,pb调用的是子类Derive中的foo()函数,输出结果:
Derive::foo
(二)非公有虚函数
和公有虚函数相对的是保护虚函数和私有虚函数,在这里,为了方便探讨问题,只讨论私有虚函数。至于保护虚函数的情况和私有虚函数大同小异,差异只在于函数的可见性不同。
从表面看,虚函数的作用就是为了使父类指针能够访问到子类对象的函数。如果将虚函数设置为私有的,那么,无论子类对象,还是父类对象,都无法访问到该函数。这样的函数就变得毫无意义了。实际情况果真如此吗?答案是否定的。首先C++支持私有虚函数,当然也支持保护虚函数,在这里,对保护虚函数不做讨论。
学习过设计模式的朋友都知道,封装算法这一设计原则。该原则使用模板方法模式,向外部提供访问的接口,该接口明确了算法的步骤。对于算法的每个步骤,由外部通过继承的方式来指定。下面举个简单的例子来说明。
众所周知,购物的过程是,先挑选商品,接着付款,最后离开商店。在这个过程中,3个步骤是有顺序的,不能随意地颠倒次序。在生活中,这个类似的例子很多。以下编写一个表示购物的抽象类,它拥有3个私有纯虚函数choose(),pay()和lease(),分别表示购物的挑选商品,付款和离开商店的3个过程,由shopping函数依次调用这3个函数:
class Store
{
public:
void shopping()
{
cout<<"Base::shopping"<<endl;
choose();
pay();
lease();
}
private:
virtual void choose() = 0;
virtual void pay() = 0;
virtual void lease() = 0;
};
接下来,实现一个表示购买水果的类,它继承自Store类,覆盖了Store类中的choose(),pay()和lease()函数。代码如下所示:
class FruitStore:public Store
{
private:
void choose()
{
cout<<"FruitStore::choose"<<endl;
}
void pay()
{
cout<<"FruitStore::pay"<<endl;
}
void lease()
{
cout<<"FruitStore::lease"<<endl;
}
};
在main函数中,编写测试代码:
Store *ps = new FruitStore();
ps->shopping();
运行后输出结果:
Base::shopping
FruitStore::choose
FruitStore::pay
FruitStore::lease
从上面的代码可以看出,父类Store通过shopping函数规定了购物的过程,至于购物的每个步骤该做什么,由子类来指定。由于表示购物步骤的函数被声明为私有的,我们就不能通过子类对象来调用choose(),pay()或lease(),避免了出现先离开商品,再挑选商品的混乱情况。
这样,我们就可以很轻松地实现各种各样的具体购物类,例如,我们新增加一个表示购买衣服的类:
class ClothesStore:public Store
{
private:
void choose()
{
cout<<"ClothesStore::choose"<<endl;
}
void pay()
{
cout<<"ClothesStore::pay"<<endl;
}
void lease()
{
cout<<"ClothesStore::lease"<<endl;
}
};
在main函数中,编写测试代码:
Store *ps = new FruitStore();
ps->shopping();
ps = new ClothesStore();
ps->shopping();
运行后输出结果:
Base::shopping
FruitStore::choose
FruitStore::pay
FruitStore::lease
Base::shopping
ClothesStore::choose
ClothesStore::pay
ClothesStore::lease
这个购买衣服类同样遵循了,先挑选商品,接着付款,最后离开商店的次序。这正是非公有虚函数和封装算法的奇妙之处。
分享到:
相关推荐
### C++虚函数与虚函数表的理解 #### 一、虚函数的概念 在C++中,虚函数(Virtual Function)是一种特殊类型的成员函数,它允许基类指针或引用指向派生类对象,并通过该基类指针或引用调用派生类中重写的同名函数。...
高质量的C++多态讲解,详细讲解虚函数,虚函数表,虚函数继承,虚函数继承下的内存分配等
在C++编程语言中,虚函数(Virtual Functions)和多态(Polymorphism)是面向对象编程的重要特性,它们使得程序具有高度的灵活性和可扩展性。本文将深入探讨这两个概念,结合示例代码进行详细解释。 首先,我们来...
在C++中,非虚析构函数只会销毁对象自身,而不会触及其派生类的成员。为了确保在删除基类指针时正确地清理派生类资源,我们需要声明析构函数为虚的: ```cpp class Base { public: virtual ~Base() { // 基类的析...
C++的虚函数和虚函数表是面向对象编程中实现多态性的重要机制。多态性允许通过基类指针或引用调用不同子类的重写方法,从而实现更灵活的设计和代码复用。 虚函数(Virtual Function)是基类中声明的一种特殊函数,...
2. **非虚析构函数的影响** - 不声明析构函数为虚函数。 - 指向基类对象的指针释放派生类对象。 - 结果:派生类的析构函数不会被调用,可能导致资源泄漏。 3. **虚析构函数的影响** - 将析构函数声明为虚函数。...
C++虚函数表详解 C++中的虚函数表是实现多态机制的关键组件。虚函数表(Virtual Table,简称V-Table)是一种机制,用于存储类的虚函数的地址,解决继承和覆盖的问题,使得父类的指针可以正确地调用子类的成员函数。...
### C++虚函数表解析深度剖析 在C++编程语言中,虚函数是实现多态性的关键机制之一,尤其在面向对象编程中扮演着至关重要的角色。本文将深入探讨C++虚函数表(Virtual Table,简称V-Table)的原理与实现细节,以...
### C++中虚函数的实现机制 #### 一、虚函数的概念及其重要性 在C++编程语言中,虚函数是实现多态性的关键机制之一。多态性是指同一个操作作用于不同的对象,可以有不同的解释,进而触发不同的行为。在面向对象...
`cbase` 类中包含了一个虚函数 `func()` 和一个非虚函数 `fun2()`。`cderive` 类重写了 `func()` 函数,并保留了 `fun2()` 函数。 ```cpp class cbase { public: virtual int func(int num = 1) const { std::...
在C++编程语言中,多态性和虚函数是面向对象编程的重要特性,它们允许程序员创建灵活且可扩展的代码结构。本次实验旨在深入理解和熟练运用这两个概念。实验报告的标题和描述表明,这是一个关于C++实验,特别是针对多...
总结来说,C++的虚函数机制是通过虚函数表来实现多态性,使得我们能够在运行时根据对象的实际类型动态地调用合适的函数。理解这一机制对于深入学习C++的继承和多态性至关重要,同时也有助于掌握其他依赖于此机制的...
在C++中,虚函数允许子类重写父类的功能,这是实现多态性(Polymorphism)的基础。当一个基类指针或引用指向派生类的对象时,通过调用虚函数,可以执行派生类中的版本,而不是基类的版本。这种机制使得代码更加灵活...
### C++虚函数调用机制初探 #### 引言 C++作为一种强大的面向对象编程语言,其核心特性之一便是支持多态性(polymorphism)。多态性允许程序员使用基类类型的指针或引用调用派生类的成员函数,这种能力极大地增强了...
虚函数调用相比非虚函数会稍慢,因为需要在运行时查找虚函数表。但在大多数情况下,这种性能差异可以忽略不计。 通过理解并熟练应用这些技巧,可以充分利用C++的多态性,编写出更加灵活和可扩展的代码。虚函数是...
1. **仅对公有成员有效**:虚函数必须是基类的公有成员,私有或受保护的成员函数不能声明为虚函数。 2. **默认构造函数非虚**:构造函数是特殊的成员函数,不能声明为虚函数。不过,析构函数默认就是虚的,这确保了...
在C++编程语言中,虚函数表(Virtual Function Table,简称VFT或VTbl)是实现多态性的重要机制,特别是在面向对象编程中。多态性允许子类重写父类的方法,使得相同的消息可以有不同的行为。虚函数表是这种功能的底层...