24-8 隐藏继承的功能

更改继承成员的访问级别

C++允许我们在派生类中修改继承成员的访问限定符。具体实现方式是使用using声明using declaration标识派生类中需更改访问权限的基类成员(该成员具有作用域),并为其赋予新的访问限定符。

例如,考虑以下基类:

#include <iostream>

class Base
{
private:
    int m_value {};

public:
    Base(int value)
        : m_value { value }
    {
    }

protected:
    void printValue() const { std::cout << m_value; }
};

由于基类 Base::printValue() 被声明为受保护(protected),因此仅能由基类 Base 或其派生类调用。公共(public)无法访问该方法。

现在定义一个派生类,将 printValue() 的访问限定符改为公共(public):

class Derived: public Base
{
public:
    Derived(int value)
        : Base { value }
    {
    }

    // Base::printValue was inherited as protected, so the public has no access
    // But we're changing it to public via a using declaration
    using Base::printValue; // note: no parenthesis here
};

这意味着以下代码现在可以正常运行:

int main()
{
    Derived derived { 7 };

    // printValue is public in Derived, so this is okay
    derived.printValue(); // prints 7
    return 0;
}

image

您只能修改派生类通常能够访问的基类成员的访问限定符。因此,您永远无法将基类成员的访问限定符从私有改为受保护或公有,因为派生类无法访问基类的私有成员。


隐藏功能

在C++中,除了修改源代码外,无法从基类中移除或限制功能。然而,在派生类中,可以隐藏基类中存在的某些功能,使其无法通过派生类访问。这只需通过更改相关的访问限定符即可实现。

例如,我们可以将公共成员改为私有:

#include <iostream>

class Base
{
public:
	int m_value{};
};

class Derived : public Base
{
private:
	using Base::m_value;

public:
	Derived(int value) : Base { value }
	{
	}
};

int main()
{
	Derived derived{ 7 };
	std::cout << derived.m_value; // error: m_value is private in Derived

	Base& base{ derived };
	std::cout << base.m_value; // okay: m_value is public in Base

	return 0;
}

image

这使我们能够将设计不良的基类数据封装在派生类中。或者,与其公开继承基类的成员并通过覆盖访问限定符将m_value设为私有,我们本可采用私有继承方式——这将使基类所有成员从一开始就以私有形式被继承。

但需注意:尽管m_value在派生类中是私有的,它在基类中仍是公有的。因此通过将派生类强制转换为Base&并直接访问成员,仍可绕过m_value在派生类中的封装机制。

---block start---

对于高级读者:
基于相同原因,若基类(Base)包含公共虚函数,而派生类(Derived)将访问限定符改为私有,公共对象仍可通过将派生对象强制转换为基类指针(Base&)并调用虚函数的方式访问该私有派生函数。编译器允许此操作,因为该函数在基类中是公有的。但由于对象实际属于派生类,虚拟函数解析将定位(并调用)派生类的(私有)版本。访问控制在运行时不会强制执行。

#include <iostream>

class A
{
public:
    virtual void fun()
    {
        std::cout << "public A::fun()\n";
    }
};

class B : public A
{
private:
    virtual void fun()
    {
         std::cout << "private B::fun()\n";
   }
};

int main()
{
    B b {};
    b.fun();                  // compile error: not allowed as B::fun() is private
    static_cast<A&>(b).fun(); // okay: A::fun() is public, resolves to private B::fun() at runtime

    return 0;
}
---block end---

令人惊讶的是,即使基类中存在一组重载函数,也无法单独修改某个重载函数的访问限定符。你只能同时修改所有重载函数的访问限定符:

#include <iostream>

class Base
{
public:
    int m_value{};

    int getValue() const { return m_value; }
    int getValue(int) const { return m_value; }
};

class Derived : public Base
{
private:
	using Base::getValue; // make ALL getValue functions private

public:
	Derived(int value) : Base { value }
	{
	}
};

int main()
{
	Derived derived{ 7 };
	std::cout << derived.getValue();  // error: getValue() is private in Derived
	std::cout << derived.getValue(5); // error: getValue(int) is private in Derived

	return 0;
}

image


在派生类中删除函数

您还可以在派生类中将成员函数标记为已删除,这确保它们完全无法通过派生对象调用:

#include <iostream>
class Base
{
private:
	int m_value {};

public:
	Base(int value)
		: m_value { value }
	{
	}

	int getValue() const { return m_value; }
};

class Derived : public Base
{
public:
	Derived(int value)
		: Base { value }
	{
	}


	int getValue() const = delete; // mark this function as inaccessible
};

int main()
{
	Derived derived { 7 };

	// The following won't work because getValue() has been deleted!
	std::cout << derived.getValue();

	return 0;
}

image

在上例中,我们已将 getValue() 函数标记为已删除。这意味着当尝试调用派生版本的函数时,编译器会发出警告。请注意,基类的 getValue() 版本仍然可用。我们可以通过以下两种方式调用 Base::getValue():

int main()
{
	Derived derived { 7 };

	// We can call the Base::getValue() function directly
	std::cout << derived.Base::getValue();

	// Or we can upcast Derived to a Base reference and getValue() will resolve to Base::getValue()
	std::cout << static_cast<Base&>(derived).getValue();

	return 0;
}

image

若采用转换方法,我们应将派生类转换为Base&而非Base,以避免复制派生类的基类部分。

posted @ 2026-02-01 07:14  游翔  阅读(0)  评论(0)    收藏  举报