问题描述
近日看代码时发现标准库里面有一个带 private 的纯虚函数,感到十分困惑,基类的 private 成员派生类无法访问,为什么要声明 private 的纯虚函数。
class memory_resource
{
static constexpr size_t _S_max_align = alignof(max_align_t);
public:
memory_resource() = default;
memory_resource(const memory_resource&) = default;
virtual ~memory_resource(); // key function
memory_resource& operator=(const memory_resource&) = default;
[[nodiscard]]
void*
allocate(size_t __bytes, size_t __alignment = _S_max_align)
__attribute__((__returns_nonnull__,__alloc_size__(2),__alloc_align__(3)))
{ return do_allocate(__bytes, __alignment); }
void
deallocate(void* __p, size_t __bytes, size_t __alignment = _S_max_align)
__attribute__((__nonnull__))
{ return do_deallocate(__p, __bytes, __alignment); }
bool
is_equal(const memory_resource& __other) const noexcept
{ return do_is_equal(__other); }
private:
virtual void*
do_allocate(size_t __bytes, size_t __alignment) = 0;
virtual void
do_deallocate(void* __p, size_t __bytes, size_t __alignment) = 0;
virtual bool
do_is_equal(const memory_resource& __other) const noexcept = 0;
};
参考资料 1:C++ 为什么会有 private virtual 函数,这样写好吗? - V2EX
用 private 还是 protected 完全取决于你自己的需要啊,这一点是不要纠结的。
基类中标记为 private 的纯虚函数是没有实现的,你要继承这样的基类就必须自己实现一个与纯虚函数同名的函数。也就是实际上可以理解为是派生类内部独立声明的一个有 private (或者其他属性)的函数,只是与基类中要求的同名而已。所以无论是哪种属性派生类中纯虚函数的实现是可以被派生类自己访问的。
这两者 private 还是 protected 只在多继承中会产生区别。
按照你的示例代码举个例子:
GameCharacter 派生出一个类 RPGGameCharacter。
RPGGameCharacter 这个类的成员函数可以自由的使用 doHealthValue()
RPGGameCharacter 又派生出一个 MMORPGGameCharacter。
MMORPGGameCharacter 这个类的成员函数则不能使用 doHealthValue,因为 doHealthValue 是 private 的,如果声明的时候标记为 protected 那么就可以。
参考资料 2:c++ - What is the point of a private pure virtual function? - Stack Overflow
测试
/*********************************************************************************
* @copyright Copyright (c) 2024
*
* @author W Wu (ww1820@qq.com)
* @version 0.1
* @date 2024-03-29
*
* @file private_virtual.cpp
* @brief private 属性的虚函数的作用
********************************************************************************/
#include <iostream>
class Base
{
public:
virtual ~Base() = default;
void test1()
{
func1();
func2();
func3();
}
virtual void test() = 0;
protected:
virtual void func1() = 0;
private:
virtual void func2() = 0;
virtual void func3() { std::cout << "Base::func3" << std::endl; }
};
class Derived : public Base
{
public:
virtual void test() override
{
func1();
func2();
// Derived 未实现,不可见
// func3();
}
protected:
virtual void func1() override { std::cout << "Derived::func1" << std::endl; }
private:
virtual void func2() override { std::cout << "Derived::func2" << std::endl; }
};
class DDerived : public Derived
{
public:
virtual void test() override
{
func1();
// DDerived 未实现,不可见
// func2();
// func3();
}
protected:
virtual void func1() override { std::cout << "DDerived::func1" << std::endl; }
};
int main(int argc, char const* argv[])
{
/* code */
Base* ptrD = new Derived();
std::cout << "=== ptrD->test1(): " << std::endl;
ptrD->test1();
std::cout << "=== ptrD->test(): " << std::endl;
ptrD->test();
Base* ptrDD = new DDerived();
std::cout << "=== ptrDD->test1(): " << std::endl;
ptrDD->test1();
std::cout << "=== ptrDD->test(): " << std::endl;
ptrDD->test();
delete ptrD;
ptrD = nullptr;
delete ptrDD;
ptrDD = nullptr;
return 0;
}
输出:
总结
虚函数(不管是不是纯虚函数)的声明为 private 是为了阻止派生类访问基类的实现,而派生类可以有自己的实现。虚函数不管访问性是 public、protected 还是 private,都能实现多态,受影响的只有外部可见性。