可以将基类的单个成员的状态更改为private吗?

Can I change the status of individual members of a base class to private?

本文关键字:private 成员 基类 单个 状态      更新时间:2023-10-16

我正在使用wxWidgets,如果您曾经使用过它,您将知道在基类中有许多公共函数。我最近遇到了一种情况,我不希望从派生类直接调用方法SetText()。也就是说,派生类继承了SetText()函数,但我不希望这个函数对客户端可用。相反,我提供了两个调用SetText()的新函数,但不是在执行一些额外的操作之前。

目前,客户端(我!)可以忘记调用特殊函数,只需调用SetText()。因此,一些额外的操作将不会被执行。这些操作非常微妙,很容易被忽略。

那么,我可以将单个函数标记为私有的,这样客户端就不可能调用它们,或者简单地使客户端不可能直接调用它(他们将不得不使用我的函数来间接调用它)?

注意SetText()而不是虚拟的。

EDIT:对于将来遇到这个问题的程序员,请检查标记的答案和Doug T。的答案。

实际上有两种方法可以做到这一点。Doug T.对第一种方法做了一个很好的概述——使用私有/受保护的继承和组合——所以我就不深入讨论了。

使用私有/受保护继承的问题是它掩盖了所有的内容。然后,您必须有选择地公开您仍然希望公开的成员。如果你想让所有的事情都公开,并且只想掩盖其中的一件事,那么这可能会成为一个令人头疼的问题。这就需要第二种方法来实现这一点——使用using关键字。

例如,在您的例子中,您可以简单地如下声明您的类:

class Child : public Parent
{
    //...
    private:
        using Parent::SetText; // overrides the access!
};

这个只有掩码SetText方法!

请记住,指向Child的指针总是可以转换为指向Parent的指针,并且可以再次访问该方法-但这也是继承的问题:

class Parent
{
public:
    void SomeMethod() { }
    void AnotherMethod() { }
};
class ChildUsing : public Parent
{
private:
    using Parent::SomeMethod;
};
class ChildPrivateInheritance : private Parent
{
};
void main()
{
    Parent *p = new Parent();
    ChildUsing *a = new ChildUsing();
    ChildPrivateInheritance *b = new ChildPrivateInheritance();
    p->SomeMethod();             // Works just fine
    a->SomeMethod();             //  !! Won't compile !!
    a->AnotherMethod();          // Works just fine
    ((Parent*)a)->SomeMethod();  // Compiles without a problem
    b->SomeMethod();             //  !! Won't compile !!
    b->AnotherMethod();          //  !! Won't compile !!
    ((Parent*)b)->SomeMethod();  // Again, compiles fine
    delete p; delete a; delete b;
}

试图访问ChildUsing实例上的SomeMethod会产生(在VS2005中):

error C2248: 'ChildUsing::SomeMethod' : cannot access private member declared in class 'ChildUsing'

然而,在ChildPrivateInheritance的实例上访问SomeMethod AnotherMethod会产生:

error C2247: 'Parent::SomeMethod' not accessible because 'ChildPrivateInheritance' uses 'private' to inherit from 'Parent'
  1. 你可以通过私有继承将每个 public设置为private,并公开你想要的接口。

    class YourWidget : private BaseClass
    {
    };
    YourWidget widget;
    widget.SetText(); // this is private
    

    这阻止了您遍历基类,并破坏了"is-a"关系。以下代码无法编译:

    BaseClass* ptr = new YourWidget(); //error, no conversion available 
    

    它也影响了BaseClass的每个公共成员,我不确定它是否适合wxWidgets的工作方式。

  2. 您可以通过在派生类中创建私有版本来隐藏SetText。这仅在通过派生类指针访问时有效,并且在通过基类指针访问时不提供任何安全性。如果客户端有基类ptr,则仍然会调用基类SetText

  3. 您可以包装BaseClass,而不是从它派生,并公开您想要的接口。这可能是可能的,也可能是不可能的,这取决于代码的工作方式。如果你想要正常的接口,但稍微改变一下,那么你就必须重新实现你想要转发到BaseClass的每个功能。

  4. 你可以让YourWidget从第二个接口继承,这个接口提供了你想要暴露给你的代码的接口,并传递指针到这个接口,而不是指针到你的BaseClass。例如

     class IMyWidget
     {
     public:
         virtual void SpecialSetText() = 0;
     }
     class YourWidget : public BaseClass, public IMyWidget
     {
     public:
         void SpecialSetText() {/*use SetText in a special way*/}
     };
    

    然后在你的代码中,你可以传递IMyWidget指针而不是BaseClass/YourWidget指针。这为代码的特定部分提供了一个进入YourWidget的特定接口,并防止(禁止向下转换)接口的其他部分被使用。

根据您希望通过派生类拥有的访问权限,您可以使用私有(或受保护)继承,这将不允许非友类访问继承的成员/方法,或者通过在派生类中声明一个新方法来隐藏基方法,该方法要么重定向,要么(如果您需要额外的参数或其他)错误