C++ prevent inherit specific public member of base class

时间:2022-09-24 23:41:59

How can I prevent a public member to be inherited by it's derived class? Like, if I have this:

如何防止公共成员被其派生类继承?就像,如果我有这个:

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) { }
};

The reason I want to do this is, I want to be able to instantiate a person class with something like:

我想这样做的原因是,我希望能够用以下内容实例化一个人类:

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

But, because the enum is public this is also available:

但是,因为枚举是公开的,所以也可以:

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

which doesn't make any sense anymore. How can I prevent the derived class to be able to access those enum values, but make it available for public from the base class?

这已经没有任何意义了。如何防止派生类能够访问这些枚举值,但是可以从基类中公开它?

4 个解决方案

#1


2  

If you insist on keeping the enum within the base class, you can place the enum in a protected section.

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

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 () {}
        //...
};

A class that derives from Person publicly will still have access to the enum values, but users of the derived class will not.

公开派生自Person的类仍然可以访问枚举值,但派生类的用户不会。

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

Thus, a Male can use MALE, but users of Male will not see Male::MALE, Male::FEMALE, nor Male::BOTH, which was one of your original issues. As to your questions, you don't really want to deny the derived class access, since you want the derived class to be able to specify the Gender. And you should not really allow any direct users of Person. Instead, a user of Person picks one of the correct derived classes, which will then establish Gender properly.

因此,男性可以使用男性,但男性用户不会看到男性::男性,男性::女性,也不会看男性:: BOTH,这是您最初的问题之一。至于你的问题,你真的不想拒绝派生类访问,因为你希望派生类能够指定Gender。你不应该真正允许任何直接用户的人。相反,Person的用户选择一个正确的派生类,然后将正确地建立性别。

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");

#2


0  

The comments in the OP suggest a rethinking of your design, and probably that is the best alternative.

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

Still, if you want to keep up with what you wrote in the answer, one possibility is to derive protectedly from your base class

尽管如此,如果你想跟上你在答案中所写的内容,一种可能性是从你的基类中得到保护

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
}

#3


0  

the problem seems to be a literate one, there is no problem with the code per se, but in viewing the code as a language reflecting common notions.

问题似乎是一个有文化的问题,代码本身没有问题,但在查看代码作为反映常见概念的语言。

In this sense i would suggest the following answer:

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

have a factory (or factory method) where the enum is public only in that factory. In other words create a new factory Human and have all base and derived classes using Human enums. Then the base class can still be Person but Person does not have any enum MALE/FEMALE/etc.. only the Human factory does, and it is not a base class that is subclassed. This would result in code which can be read literaly like this:

有一个工厂(或工厂方法),其中枚举仅在该工厂公开。换句话说,创建一个新工厂Human,并使用Human枚举包含所有基类和派生类。然后基类仍然可以是Person但是Person没有任何枚举MALE / FEMALE / etc ..只有Human工厂才有,并且它不是子类的子类。这将导致代码可以像下面这样读取:

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

You can also use Gender factory (instead of Human) like this (to be even more literate and separate the inter-dependencies):

你也可以像这样使用性别工厂(而不是人工)(为了更有文化并将相互依赖性分开):

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

Note that a base class includes the lowest common denominator that all derived classes must share. So since the derived classes Male Female etc are not supposed to share the gender enum then it should not be part of the base class, but a class or construct in itself which is then used by Person classes and its derivatives.

请注意,基类包括所有派生类必须共享的最小公分母。因此,由于派生类Male Female等不应该共享性别枚举,所以它不应该是基类的一部分,而是本身的一个类或构造,然后由Person类及其派生类使用。

#4


-1  

Simple make the Person class abstract using a protected constructor:

使用受保护的构造函数简单地使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)
        {
        }
};

If the gender is anyway independent from the Person class, just move it out of the class:

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

enum Gender {
    GenderMale,
    GenderFemale,
    GenderBoth
};

class Person //...
class Male // ...

You can also encapsulate the Gender into a own class:

您还可以将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;
};

#1


2  

If you insist on keeping the enum within the base class, you can place the enum in a protected section.

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

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 () {}
        //...
};

A class that derives from Person publicly will still have access to the enum values, but users of the derived class will not.

公开派生自Person的类仍然可以访问枚举值,但派生类的用户不会。

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

Thus, a Male can use MALE, but users of Male will not see Male::MALE, Male::FEMALE, nor Male::BOTH, which was one of your original issues. As to your questions, you don't really want to deny the derived class access, since you want the derived class to be able to specify the Gender. And you should not really allow any direct users of Person. Instead, a user of Person picks one of the correct derived classes, which will then establish Gender properly.

因此,男性可以使用男性,但男性用户不会看到男性::男性,男性::女性,也不会看男性:: BOTH,这是您最初的问题之一。至于你的问题,你真的不想拒绝派生类访问,因为你希望派生类能够指定Gender。你不应该真正允许任何直接用户的人。相反,Person的用户选择一个正确的派生类,然后将正确地建立性别。

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");

#2


0  

The comments in the OP suggest a rethinking of your design, and probably that is the best alternative.

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

Still, if you want to keep up with what you wrote in the answer, one possibility is to derive protectedly from your base class

尽管如此,如果你想跟上你在答案中所写的内容,一种可能性是从你的基类中得到保护

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
}

#3


0  

the problem seems to be a literate one, there is no problem with the code per se, but in viewing the code as a language reflecting common notions.

问题似乎是一个有文化的问题,代码本身没有问题,但在查看代码作为反映常见概念的语言。

In this sense i would suggest the following answer:

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

have a factory (or factory method) where the enum is public only in that factory. In other words create a new factory Human and have all base and derived classes using Human enums. Then the base class can still be Person but Person does not have any enum MALE/FEMALE/etc.. only the Human factory does, and it is not a base class that is subclassed. This would result in code which can be read literaly like this:

有一个工厂(或工厂方法),其中枚举仅在该工厂公开。换句话说,创建一个新工厂Human,并使用Human枚举包含所有基类和派生类。然后基类仍然可以是Person但是Person没有任何枚举MALE / FEMALE / etc ..只有Human工厂才有,并且它不是子类的子类。这将导致代码可以像下面这样读取:

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

You can also use Gender factory (instead of Human) like this (to be even more literate and separate the inter-dependencies):

你也可以像这样使用性别工厂(而不是人工)(为了更有文化并将相互依赖性分开):

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

Note that a base class includes the lowest common denominator that all derived classes must share. So since the derived classes Male Female etc are not supposed to share the gender enum then it should not be part of the base class, but a class or construct in itself which is then used by Person classes and its derivatives.

请注意,基类包括所有派生类必须共享的最小公分母。因此,由于派生类Male Female等不应该共享性别枚举,所以它不应该是基类的一部分,而是本身的一个类或构造,然后由Person类及其派生类使用。

#4


-1  

Simple make the Person class abstract using a protected constructor:

使用受保护的构造函数简单地使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)
        {
        }
};

If the gender is anyway independent from the Person class, just move it out of the class:

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

enum Gender {
    GenderMale,
    GenderFemale,
    GenderBoth
};

class Person //...
class Male // ...

You can also encapsulate the Gender into a own class:

您还可以将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;
};