[effective C++] 条款36: 绝不重新定义继承而来的non-virtual 函数

在 C++ 中,重新定义继承而来的 non-virtual 函数(非虚函数)通常是不推荐的,特别是在遵循《Effective C++》条款 36 的建议时。以下是条款 36 的核心内容和原因解释:

条款 36:绝不重新定义继承而来的 non-virtual 函数

1. non-virtual 函数的定义和行为

non-virtual 函数是在基类中定义的普通成员函数,不带有 virtual 关键字。在子类中定义同名同参数的函数时,实际上是隐藏(隐藏是指基类函数名被隐藏,无法在子类作用域中直接访问,但可以通过作用域解析操作符调用)了基类的函数,而不是覆盖它。这意味着通过基类指针或引用调用该函数时,调用的是基类版本,而不是子类版本。

2. 为何不重新定义 non-virtual 函数

重新定义继承而来的 non-virtual 函数可能会导致以下问题:

  • 函数调用行为的困惑: 由于 non-virtual 函数的多态性不适用于子类,使用基类指针或引用调用函数时,总是调用基类版本的函数。这与虚函数的行为不同,容易让开发者混淆和误解。
  • 隐藏问题: 如果子类中定义了与基类同名的 non-virtual 函数,它会隐藏基类中的所有同名函数,无论这些函数的参数列表是否相同。这可能导致基类中的函数无法在子类中被正常访问,造成潜在的问题。
  • 可维护性和可读性: 重定义 non-virtual 函数会导致代码可读性和维护性变差,因为非专业的行为和意图不容易被理解。尤其在大型代码库中,随着时间推移和人员更替,代码的可维护性会受到严重影响。
3. 推荐的做法
  • 保持 non-virtual 函数的独特性: 继承的 non-virtual 函数不应被重定义。如果需要修改函数的行为,应当使用虚函数机制。
  • 使用虚函数实现多态: 如果基类中的某个函数可能在派生类中需要不同实现,应当将其声明为虚函数。虚函数使得调用能够根据对象的实际类型(而非指针或引用的静态类型)选择适当的函数版本。
4. 总结

避免重新定义继承而来的 non-virtual 函数的原则有助于保持代码行为的一致性和可预测性,减少混乱和错误的可能性。虚函数的使用提供了一种明确而安全的多态机制,使代码更易于理解和维护。
遵循这些原则可以帮助开发者编写更健壮和可维护的 C++ 代码。

5.补充(动态绑定和静态绑定)

只有虚函数才会被动态绑定, 非虚函数是静态绑定

动态绑定与静态绑定

1. 静态绑定(Static Binding)

静态绑定发生在编译时,函数的调用与函数的定义在编译时就已经确定。非虚函数通常是静态绑定的。这意味着,通过对象或指针调用非虚函数时,编译器会在编译时决定调用哪个函数版本。无论实际对象的类型是什么,调用的始终是指针或引用的静态类型所指向的类中的那个非虚函数。
示例

cpp
复制代码
class Base {
public:
    void nonVirtualFunction() {
        std::cout << "Base::nonVirtualFunction" << std::endl;
    }
};

class Derived : public Base {
public:
    void nonVirtualFunction() {
        std::cout << "Derived::nonVirtualFunction" << std::endl;
    }
};

Base* b = new Derived();
b->nonVirtualFunction();  // 输出 "Base::nonVirtualFunction"

在上面的例子中,无论 b 实际上指向 Derived 对象,nonVirtualFunction 的调用都会静态绑定到 Base 类中的实现。

2. 动态绑定(Dynamic Binding)

动态绑定发生在运行时,它是通过虚函数实现的。当基类中声明函数为 virtual 时,在子类中可以覆盖它。调用该函数时,系统会根据实际对象的类型在运行时选择相应的函数版本,即使调用是通过基类指针或引用进行的。
示例

cpp
复制代码
class Base {
public:
    virtual void virtualFunction() {
        std::cout << "Base::virtualFunction" << std::endl;
    }
};

class Derived : public Base {
public:
    void virtualFunction() override {
        std::cout << "Derived::virtualFunction" << std::endl;
    }
};

Base* b = new Derived();
b->virtualFunction();  // 输出 "Derived::virtualFunction"

在这个例子中,由于 virtualFunction 是虚函数,调用 b->virtualFunction() 时会动态绑定到 Derived 类中的实现,即使 b 的静态类型是 Base*

总结
  • 静态绑定:编译时确定函数调用,与对象的实际类型无关,适用于非虚函数。
  • 动态绑定:运行时确定函数调用,根据对象的实际类型选择函数版本,适用于虚函数。
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值