【问题标题】:C++ private and protected virtual methodC++ 私有和受保护的虚方法
【发布时间】:2013-01-18 04:48:49
【问题描述】:

似乎最好将虚拟方法设为私有,以便将以下两个客户端的接口分开 - 1. 实例化对象并调用方法的客户端 2. 从类派生并可能想要覆盖该方法的客户端。 简单地说——第一个客户端不需要知道一个方法是否是虚拟的。他将调用基类公共非虚拟方法,而后者又将调用私有虚拟方法。例如,请参见下面的代码。

现在,如果虚拟方法需要对其基类的相应虚拟方法进行超级消息传递,例如 Save 方法 - 它必须通过继承链中的所有虚拟方法才能保存对应的数据每个派生级别 - 我们别无选择,只能使用受保护的虚拟方法 - 除非有一种方法可以保证在不使用超级消息传递的情况下在所有派生级别保存数据(我不知道)。

我想知道上述推理是否正确。

确保您使用滚动条来查看整个代码。

#include <iostream>
using namespace std;

class A {

    string data;    

protected:

    virtual void SaveData()= 0;

public:

    A():data("Data of A"){}

    void Save(){
        cout << data << endl;        
        SaveData();
    }
};

class B : public A {

    string data;

protected:

    virtual void SaveData() { cout << data << endl;}

public:

    B():data("Data of B") {}

};

class C : public B {

    string data;
protected:

    virtual void SaveData() {
        B::SaveData();
        cout << data << endl;
    }

public:

    C():data("Data of C") {}
};


int main(int argc, const char * argv[])
{
    C c;
    c.Save();

    return 0;
}

【问题讨论】:

    标签: c++


    【解决方案1】:

    是的,如果您需要调用另一个类的 SaveData,则需要从该类访问它 - 所以 publicprotected

    【讨论】:

    • 免费为我 +1
    【解决方案2】:

    你说得对:

    • NVI(非虚拟接口)要求 virtual 方法不是 public
    • 调用基类方法要求它不是private

    因此protected 是显而易见的解决方案,至少在 C++03 中是这样。不幸的是,这意味着您必须相信派生类开发人员不会忘记调用“super”。


    在 C++11 中,您可以使用final 来防止派生类覆盖virtual 方法;这意味着尽管你不得不引入一个新的钩子,例如:

    class Base {
    public:
        void save() {
            // do something
            this->saveImpl();
            // do something
        }
    private:
        virtual void saveImpl() {}
    };
    
    class Child: public Base {
    private:
         virtual void saveImpl() final {
             // do something
             this->saveImpl2();
             // do something
         }
         virtual void saveImpl2() {}
    };
    

    当然,每次都必须想出一个新名称是很麻烦的……但至少可以保证Child::saveImpl 会被调用,因为它的子级都不能覆盖它。

    【讨论】:

    • 为什么NVI要求虚函数是非公开的?
    • @Rich:在 C++ 中,当派生类重写一个方法时,它会完全替换它; NVI 用于确保基类保留与虚拟方法不兼容的一些控制(例如,获取锁、检查不变量等)。有关详细信息,请参阅en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-Virtual_Interface
    • 感谢您的链接,我想我理解这个成语背后的寓意。当您说 NVI 要求虚拟方法不是 public 时,我只是不太明白。除了将客户端(公共)接口与实现(非公共)接口分开的准则之外,我看不出为什么公共虚拟会阻止 NVI 工作。在上面的例子中声明 saveImpl() public 是不是灾难性的?
    • @Rich: saveImplfinal(因此不再是虚拟的),不是 :)
    • @MatthieuM。哦,我错过了final 部分。但是您的解决方案特定于 OP 的需求,即在每个派生级别上强制传播和处理。在传统的 NVI 习惯中,任何派生类都可以覆盖 saveImpl,为什么 saveImpl 不能公开,除非它违反了将接口与实现分离的 OO 原则。
    【解决方案3】:

    很难说出您在问什么,但从示例中,您不需要使方法受到保护。它实际上可以是私有的。有关细节的详细信息,请参阅此帖子:What is the point of a private pure virtual function?

    只要您不从派生类(或外部类)调用私有成员,就可以了。覆盖私人成员是可以的。您可以覆盖父母的私人信息听起来确实很顽皮和错误,但在 c++ 中您可以这样做。

    以下应该没问题:

    #include <iostream>
    using namespace std;
    
    class A {
    
        string data;    
    
    private:
    
        virtual void SaveData()= 0;
    
    public:
    
        A():data("Data of A"){}
    
        void Save(){
            cout << data << endl;        
            SaveData();
        }
    };
    
    class B : public A {
    
        string data;
    
    private:
    
        virtual void SaveData() { cout << data << endl;}
    
    public:
    
        B():data("Data of B") {}
    
    };
    

    【讨论】:

    • 这是覆盖而不是重载
    • 但是,这并不能解决调用基类的 SaveData 成员的问题,我相信这是最初的问题!
    • @MatsPetersson 看起来问题已被编辑。正如我所说,很难知道要问什么。
    猜你喜欢
    • 2011-07-09
    • 2011-06-14
    • 2012-01-09
    • 2019-02-10
    • 2013-12-14
    • 2023-04-08
    相关资源
    最近更新 更多