簡體   English   中英

僅通過公共繼承暴露某些方法

[英]exposing only certain methods through public inheritence

我有一個基類,說Employee有一些方法。 我稍后會派出一些子類,如ManagerDeveloperDesigner等,這些子類也是員工(因為繼承)。 現在說代碼看起來像 -

#include <iostream>
#include <vector>

class Employee{
    private : char name[5] = "abcd";
              void allDept(){ std::cout<<"Woo"; }

    public: void tellName(){std::cout << name << "\n"; }
            void showEveryDept(){std::cout<< "Employee can see every dept\n"; 
                                 allDept(); }
            virtual ~Employee() {}
};

class Manager: public Employee{
    private : char dept[5] = "aaaa";
    public: void showOwnDept(){std::cout<< "Manager can see own dept\n";}
};

class Designer: public Employee{
    private : char color = 'r';
    public: void showOwnDept(){std::cout<< "Designer can see own dept\n";}
};

int main(){

    Employee *E = new Designer;

    E->showEveryDept();

    // E->showOwnDept(); // will not work, but can be casted dynamically and even statically if sure, to call it!

    Designer* D = dynamic_cast<Designer*>(E);

    D->showOwnDept();
}

所以我們在這里看到的是我可以使用多態,將基類指針指向派生類對象,並仍在子類上調用基類可訪問方法。 另外,要從子類調用子類方法,我可以動態地將其轉換回來。

但是現在我想要做的是,從子類調用中隱藏一個公共類成員,這樣子類不能調用它,但是基類對象可以。 showEveryDept()為例,它可以由子類和父類調用。 但是由於Designer和Manager已經分配了他們的部門,我不希望他們訪問這個功能。

我嘗試了一種非常hacky的方法來解決這個問題,通過編寫另一層類的b / w Employee類和它的孩子,就像這樣 -

class Employee{
    private : char name[5] = "abcd";
              void allDept(){ std::cout<<"Woo"; }

    public: void tellName(){std::cout << name << "\n"; }
            void showEveryDept(){std::cout<< "Employee can see every dept\n";
                                 allDept();}
            virtual ~Employee() {}
};

class ELayer: private Employee{
    private: using Employee::showEveryDept;
    private: using Employee::tellName;
};

class Manager: public ELayer{
    private : char dept[5] = "aaaa";
    public: void showOwnDept(){std::cout<< "Manager can see own dept\n";}
};

class Designer: public ELayer{
    private : char color = 'r';
    public: void showOwnDept(){std::cout<< "Designer can see own dept\n";}
};

int main(){
    Employee *E = new Designer;
    E->showEveryDept();
    // E->showOwnDept(); // will not work, but can be casted dynamically
                      // and even statically if sure, to call it!
    Designer* D = dynamic_cast<Designer*>(E);
    D->showOwnDept();
}

但是看起來很聰明,它不起作用 -

 prog.cc: In function 'int main()': prog.cc:27:23: error: 'Employee' is an inaccessible base of 'Designer' Employee *E = new Designer; 

那么我的選擇是什么? 一種愚蠢的方法是將該函數設置為虛擬,但是子類不會被強制覆蓋它,如果它們忘了聲明它,它會調用父函數嗎?

但是現在我想要做的是,從子類調用中隱藏一個公共類成員,這樣子類不能調用它,但是基類對象可以。

繼承基於Liskov替換原則 簡而言之,在任何我使用Base*我應該能夠使用Derived*並且一切都應該等效。 您希望通過使派生類的基類操作格式錯誤來違反該概念。 這意味着你的抽象是錯誤的。

而且,無論如何,這樣的事情毫無意義。 您無法動態實現此類機制,並且如果您靜態實現它,以便:

Derived d;
Base* b = &d;

b->foo(); // OK
d.foo();  // error

我總能做到:

static_cast<Base&>(d).foo(); // OK, just verbose

您可能需要層次結構的第二個分支:

struct Base { };
struct SpecialDerived : Base { void foo(); };
struct NormalDerived : Base { };

現在只有SpecialDerived可以調用foo() ,但是每個SpecialDerived仍然是一個Base ,每個NormalDerived都是一個Base ,一切都運行得很順利。

聽起來像你可能需要重新考慮你的繼承層次結構。 如果派生自Employee類不能調用showEveryDept() ,那么這表明showEveryDept()不應該成為Employee一部分。 通過嘗試從派生類中的Employee公共接口中刪除方法,您將破壞子類型關系。 如果B缺少使某些東西成為A某些行為,則B 不是 A而不應該從A派生。

也許您應該添加另一個派生自Employee類,並將showEveryDept()移動到該類。 或者簡單地允許ManagerDesigner調用showEveryDept()是合適的行為。 如果不了解你的目標,我不能說。

另一種選擇是使用using聲明的孩子,與私有繼承一起,選擇性地決定你可以訪問它。 這比virtual替代方案更靈活,並且沒有任何額外開銷。 此外,它可以將公共訪問“轉換”為受保護的訪問。

class Employee
{
    private:
        char name[5] = "abcd";

        void allDept()
        {
            std::cout << "Woo";
        }

    public:
        void tellName()
        {
            std::cout << name << "\n";
        }

        void showEveryDept()
        {
            std::cout << "Employee can see every dept\n";
            allDept();
        }

        virtual ~Employee() {}
};

class Designer : private Employee
{
    private:
        char color = 'r';

    public:
        using Employee::tellName();

        void showOwnDept()
        {
            std::cout<< "Designer can see own dept\n";
        }
};

現在,您可以調用Desginer::tellName()Designer::showOwnDept() ,但Designer::showEveryDept()是私有的! 但是,缺點是您不能再從外部代碼將Designer*轉換為Employee* 您可以在Employee添加一個方法來做到這一點。 但是,您應該記得在派生類中using Employee::as_employee

class Employee
{
    public:
        Employee& as_employee()
        {
            return *this;
        }

        const Employee& as_employee() const
        {
            return *this;
        }
};

無論如何,你應該問自己這是否真的是最好的預期設計,你真的需要這樣做,或者如果在Employee中有一個(可選純的)虛擬函數showDept()更好,派生類可能(或必須) ,如果純粹的覆蓋。

編輯 :從你的評論我讀到另一個答案,我可以很容易地得出結論,你的問題是你不明白基類, Employee不會被用作某種“未分配的員工”占位符。 這樣:設計師是員工,未分配的員工是員工,但設計師不是未分配的員工。 因此,最好重新構建代碼。 無論如何,為了完整起見,我將離開上述解決方案。

在子女中隱藏父類(直接或間接)的方法並不是解決此問題的最佳方法。 例如,如果孩子的孩子想要獲得隱藏的功能怎么辦? 這是一團糟。

實現您所需要的一種方法是對其進行建模:員工是否能夠看到所有部門是員工的“屬性”。 因此,您可以添加bool屬性“CanShowEveryDept”,根據您的功能要求在父級中確定其默認值,並在每個子類構造函數中正確設置它。

class Employee
{
protected:
    bool CanShowEveryDept;
public:
    Employee()
    {
        CanShowEveryDept = true;
    }


public:
    void showEveryDept()
    {
        if (!CanShowEveryDept)
            return;

        std::cout << "Employee can see every dept\n";
        allDept();
    }
};

class Designer : private Employee
{
public:
    Designer()
    {
        CanShowEveryDept = false;
    }
};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM