c++防止继承基类的特定公共成员

C++ prevent inherit specific public member of base class

本文关键字:成员 继承 基类 c++      更新时间:2023-10-16

如何防止公共成员被它的派生类继承?比如,如果我有这个:

class Person {
    public:
        enum { MALE, FEMALE, BOTH };
        Person(std::string name, int gender)
            : _name(name)
        {
            switch (gender) { /* processing here */ }
        }
    protected:
        std::string _name;
};
class Male : public Person {
    public:
        Male(std::string name)
            : _name(name, 0) { }
};

我想这样做的原因是,我希望能够实例化一个person类,如:

Person person("The Name", Person::MALE);

但是,由于enum是公共的,这也是可用的:

Male::MALE
Male::FEMALE
Male::BOTH

已经没有意义了。如何防止派生类能够访问这些枚举值,但使其可从基类的公共?

如果您坚持将enum保留在基类中,则可以将enum放置在受保护的部分中。

class Person {
    protected:
        enum Gender { MALE, FEMALE, BOTH };
        Person(std::string name, Gender gender)
            : _name(name)
        {
            switch (gender) { /* processing here */ }
        }
        std::string _name;
    public:
        virtual ~Person () {}
        //...
};

Person公开派生的类仍然可以访问enum的值,但是派生类的用户不能。

class Male : public Person {
    public:
        Male (std::string name) : Person(name, MALE) {}
        //...
};

因此,Male可以使用MALE,但Male的用户将无法看到Male::MALE, Male::FEMALEMale::BOTH,这是您最初的问题之一。至于您的问题,您并不是真的想要拒绝派生类访问,因为您希望派生类能够指定Gender。并且不应该允许任何直接用户使用Person。相反,Person的用户选择一个正确的派生类,这将正确地建立Gender

class Female : public Person {
    public:
        Female (std::string name) : Person(name, FEMALE) {}
        //...
};
class Transgender : public Person {
    public:
        Transgender (std::string name) : Person(name, BOTH) {}
        //...
};
std::shared_ptr<Person> p = std::make_shared<Female>("Beth");

OP中的评论建议重新考虑您的设计,这可能是最好的选择。

但是,如果您想跟上您在答案中所写的内容,一种可能性是从基类

派生protected ly。
class Person {
    public:
        enum { MALE, FEMALE, BOTH };
        Person(std::string name, int gender)
            : _name(name)
        {
            switch (gender) { /* processing here */ }
        }
    protected:
        std::string _name;
};
class Male : protected Person
{          //^^^^^^^^^
    public:
        Male(std::string name)
            : Person(name, 0) { }
        void foo() { FEMALE; }  // still ok, a public field derived protected'ly is accessible
};
int main()
{
    Male male("Joe");
    male.MALE;     //error, cannot call the enum from outside the class
}

问题似乎是文字问题,代码本身没有问题,而是将代码视为反映公共概念的语言。

在这个意义上,我建议以下答案:

有一个工厂(或工厂方法),其中枚举仅在该工厂中是公共的。换句话说,创建一个新的工厂Human,并使用Human枚举拥有所有基类和派生类。然后基类仍然可以是Person,但Person没有任何enum MALE/FEMALE/等。只有Human工厂这样做,并且它不是子类化的基类。这将导致代码可以像这样字面理解:

Person person("The Name", Human::MALE);

您也可以像这样使用Gender工厂(而不是Human)(更有文化并分离相互依赖):

Person person("The Name", Gender::MALE);

注意,基类包含所有派生类必须共享的最小公分母。因此,由于衍生类Male Female等不应该共享gender enum,那么它不应该是基类的一部分,而是一个类或构造本身,然后由Person类及其衍生物使用。

使用受保护的构造函数使Person类抽象:

class Person {
public:
    enum Gender {
        MALE,
        FEMALE,
        BOTH
    };
protected:
    // Protected constructor can only be called from subclasses.
    Person(std::string name, Gender gender)
        : _name(name)
    {
        switch (gender) { /* processing here */ }
    }
protected:
    std::string _name;
};
class Male : public Person {
    public:
        Male(std::string name)
            : Person(name, MALE)
        {
        }
};

如果性别独立于Person类,只需将其移出类:

enum Gender {
    GenderMale,
    GenderFemale,
    GenderBoth
};
class Person //...
class Male // ...

还可以将Gender封装到自己的类中:

class Gender {
public:
    enum Type { Male, Female, Both };
    Gender(Type type); 
    Gender(const Gender &copy);
    Gender& operator=(Gender &assign);
public:
    bool operator==(const Gender &other) const;
    // etc.
public:
    std::string toString() const;
};