虚函数的一种替代方案

虚函数的目的是实现对象的动态绑定,但是有些情况下,可能换一种替代方案,可能会使类的设计更加稳定,易用,下面列出一个我认为很棒的虚函数的替代方案。
使用non-virtual interface(NVI)手法,它以public non-virtual 成员函数包裹较低访问性的virtual函数:

class A{
public:
    int getVal(){
        return get();
    }
private:
    virtual int get(){...}
}
class B:public A{
private:
    virtual int get(){...}
}

在代码里我们可以看到虚函数不同以往的虚函数继承措施,以往的虚函数应该是public的,然后各个派生类在各自内部以pubilc类型进行重新实现。但是上述代码中将基类中的virtual函数声明为了private类型,并用一个non-virtual函数getVal封装了虚函数get(),该non-virtual函数只在基类中实现,派生类一律继承。这样可以达到和虚函数一样的效果,一样可以进行动态绑定。那么一定会有同学问,这么皮一下很开心吗?是的,在下面说的这种情况下,很开心:

class A{
public:
    enum val{small,middle,large};
    virtual int get(val value=small){...}
}
class B:public A{
public:
    virtual int get(val value=large){...}
}
A *pa=new B();
A->get();

这个例子中,虚函数带有默认实参,我们声明A类型的指针指向一个B对象,然后调用其中的get()函数,相信十有八九众人会立刻想,虚函数!动态绑定
!可是我在编译器上实现过了,发现执行的函数是B类中的函数,但是get到的参数是A类中默认传递的参数small。这个原因是因为编译器分为静态绑定和动态绑定,也就说所谓的早绑定和晚绑定,对于虚函数,编译器确实是施以了动态绑定这个高大上的设施,但是对于其中的默认实参,编译器依然实施的是静态绑定策略。
为什么C++坚持以这种乖张的方式来运作呢?答案在于运行期效率,如果缺省值是动态绑定,编译器就必须有某种办法在运行期为virtual函数决定适当的参数缺省值。这比目前实行的“在编译期决定”的机制更慢而却更复杂。为了程序的执行速度和编译器实现上的简易度,C++做了这样的取舍,其结果就是你如今所享受的执行效率。
因此,对于虚函数,本身就不应该重定义默认实参,但是既然不能重定义默认实参,每次继承还要重新写一遍,这就给类的定义者增加了负担,也增加了出错的可能。因此对于这种情况,我们就应该祭出本文所说的新武器—NVI手法。

class A{
public:
    enum val{small,middle,large};
    int getVal(val value=small){
        return get();
    }
private:    
    virtual int get(val value){...}
}
class B:public A{
private:
    virtual int get(val value){...}
}
A *pa=new B();
A->getVal();

这个时候,我们只需要在getVal函数中设置好默认实参值,调用getVal函数的时候会转调虚函数get,get只接受getVal函数中默认实参small,保证了派生类和基类的统一性,杜绝了出错的可能。

### 虚函数与虚构造函数的概念及用法 #### 1. 虚函数概述 在 C++ 中,虚函数一种实现动态绑定或多态机制的重要工具。当基类中的某个成员函数被声明为 `virtual` 后,在派生类中重新定义该函数时,可以通过指向基类对象的指针或引用调用实际对应的派生类版本的函数[^2]。 虚函数的主要目的是支持运行时多态行为,允许子类重写父类的行为并提供自己的实现方式。这种特性使得程序更加灵活和可扩展。 ```cpp class Base { public: virtual void display() { std::cout << "Base class\n"; } }; class Derived : public Base { public: void display() override { std::cout << "Derived class\n"; } }; ``` 如果创建了一个 `Base* ptr = new Derived()` 并调用了 `ptr->display()` 方法,则会执行 `Derived` 类中的 `display` 实现而不是 `Base` 的默认实现[^3]。 --- #### 2. 构造函数是否可以是虚函数构造函数本质上是用来初始化新分配的对象实例的状态数据成员的方法,因此它无法成为虚拟方法的一部分原因在于: - **时间顺序问题**:在任何类型的对象构建之前,编译器需要知道确切要调用哪个构造函数来完成必要的内存布局工作;而此时还没有形成完整的继承链结构供决策依据。 由于这些限制因素存在,所以标准规定不允许将构造函数标记成 `virtual` 关键字修饰的形式[^1]。 然而值得注意的是虽然我们不能直接把普通的构造过程设置成虚性质,但是有一种间接手段叫做工厂模式能够达到相似效果——即利用静态成员函数返回特定类型的新建实体作为替代方案之一[^4]。 --- #### 3. 虚析构函数的作用及其必要性 尽管单独来看构造阶段不适用虚属性设定,但对于销毁操作而言情况有所不同。为了确保即使是从基类指针删除派生物件也能正确释放资源(包括清理堆上分配的数据),通常建议给拥有至少一个纯虚函数或者非纯虚函数的基础类别配备一个显式的虚析构函数定义形式如下所示: ```cpp class Base { public: virtual ~Base() {} // Virtual destructor ensures proper cleanup. }; ``` 这样做的好处是在通过基类指针删除派生类对象的时候,系统会自动找到合适的派生类析构函数去处理细节部分,从而避免潜在的风险隐患比如悬空指针等问题发生几率大大降低. --- #### 4. 所谓“虚构造函数”的概念探讨 严格意义上讲,“虚构造函数”并不是真正意义上的术语,因为正如前面提到过的那样,C++ 不支持真正的虚构造函数功能。不过有时候人们可能会提及到所谓的“仿效虚构造函数”,这实际上是指借助其他设计技巧模拟出来的类似作用机理,最常见的方式就是采用静态工厂方法配合模板技术共同协作达成目标: ```cpp #include <iostream> using namespace std; // Abstract base class with a pure virtual function and virtual dtor. struct Creator { static Creator* create(int type); virtual void identifyYourself() const =0; virtual ~Creator(){} }; // Concrete implementations of the abstract creator. struct TypeA : public Creator{ void identifyYourself()const{ cout<<"I am TYPE A"<<endl;} }; struct TypeB : public Creator{ void identifyYourself()const{ cout<<"I am TYPE B"<<endl;} }; // Static factory method implementation. Creator *Creator::create( int type ){ switch(type){ case 1:return new TypeA(); case 2:return new TypeB(); default:throw invalid_argument("Invalid argument"); } } int main(){ try{ auto p=Creator::create(1);p->identifyYourself();delete p; p=Creator::create(2);p->identifyYourself();delete p; }catch(const exception &e){cerr<<e.what();} } ``` 在这个例子当中,我们并没有真的让构造本身变成虚化状态,而是依靠外部辅助逻辑实现了按需定制不同种类的具体产物生成流程管理需求。 --- ### 总结对比表 | 特性 | 虚函数 | “虚构造函数” | |-------------------|----------------------------------|------------------------------------| | 定义位置 | 成员函数 | 非正式说法, 实际依赖于工厂模式 | | 是否受 runtime 控制 | 是 | 否 | | 主要用途 | 支持多态 | 提供统一接口用于创建不同类型对象 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值