Как сделать недоступной переменную класса

Мне интересно, можно ли сделать переменную класса недоступной внутри этого класса? Единственный способ изменить значение этой переменной — через установщик класса. Например:

class foo
{
private:
    int m_var;
    bool m_isBig;
    void setVar(int a_var)
    {
        // do something before setting value, like emitting signal
        m_var = a_var;
    }
    void method()
    {
        int copy = m_var; // ok
        m_var = 5; // error!
        setVar(101); // ok
        doSomething();
    }
    void doSomething()
    {
        if(m_var > 5)
        { m_isBig = true; }
        else
        { m_isBig = false; }
    }
};

Я знаю, что могу написать еще один класс только с сеттерами и геттерами, но тогда у меня не будет доступа к другим методам/переменным из класса foo(encapsulation!). Я думаю, что это может быть распространенной проблемой, и для этого может быть какой-то шаблон проектирования, но я не могу его найти.

РЕДАКТИРОВАТЬ: я отредактировал код, чтобы было ясно, что я хочу сделать в установщике.


person Isaac    schedule 25.09.2015    source источник
comment
comment
Вероятно, вы могли бы получить эту функциональность, как вы говорите, создав другой класс только с сеттерами и геттерами, а затем, чтобы предоставить этому дополнительному классу доступ к методам/переменным из foo, вы должны были бы foo объявить его как friend. Я ожидаю, что вы даже можете сделать это внутренним классом, возможно, только определенным в файле реализации, если вы действительно хотите скрыть его.   -  person YoungJohn    schedule 25.09.2015
comment
Почему вы хотите этого? Если у вас есть частный член, вы несете ответственность за реализацию, так в чем смысл?   -  person Jepessen    schedule 25.09.2015
comment
Ваш код не компилируется, вы имели в виду private: на самом деле?   -  person πάντα ῥεῖ    schedule 25.09.2015
comment
@πάνταῥεῖ да, отредактировано.   -  person Isaac    schedule 25.09.2015
comment
@Isaac Объявления членов вашей функции по-прежнему private и бесполезны.   -  person πάντα ῥεῖ    schedule 25.09.2015


Ответы (3)


Я не знаю шаблона для этого, но одна из возможностей - обернуть член внутри вложенного класса. Я думаю, что это также лучший стиль, поскольку создание нового типа выражает намерение, чтобы этот член был не просто целым числом, а вместо этого имел уникальное поведение.

class foo {
    class MVar {
      public:
        MVar(foo* parent, int value = 0) : m_parent(parent), m_value(value) {}
        MVar& operator=(const MVar&) = delete; // disable assignment
        operator int() const { return m_var; }
        void set(int new_value) {
            // do something, possibly with m_parent
            // nested classes have access to the parent's private members
            m_value = new_value;
        }
      private:
        foo* m_parent;
        int m_value;
    } m_var;
    void method() {
        int copy = m_var; // ok
        m_var = 5;        // error
        MVar.set(101);    // ok
    }
};

Это не совсем то, что вам нужно, поскольку m_var на самом деле не имеет типа int, но это следует учитывать.

person Brian Bi    schedule 25.09.2015
comment
почему задний указатель? почему бы не использовать простую обертку значения для int? - person TemplateRex; 25.09.2015
comment
@TemplateRex В случае, если действие, которое OP хочет выполнить при установке переменной, должно вызывать методы для родителя. ОП говорит что-то похожее на это (но вопрос не совсем ясен). - person Brian Bi; 25.09.2015
comment
это звучит слишком сложно, m_var является оболочкой int, все, что он хочет, это установить какое-то целочисленное значение. если method() нужен другой контекст, то он может получить его от класса foo, но, конечно же, m_var он бесполезен - person TemplateRex; 25.09.2015
comment
Сделать MVar другом foo проще, чем передать указатель на класс foo? - person Isaac; 25.09.2015

Вы не можете сделать именно то, что вы просите на С++. Все переменные в классе видны всем методам в классе, независимо от того, являются ли они общедоступными или закрытыми.

Вопрос, который вы хотите себе задать: зачем классу скрывать что-то от самого себя? Интерфейс класса — это граница между внутренней реализацией класса и службами, которые он предоставляет внешнему миру. Ты либо внутри класса, либо снаружи.

Имея это в виду, возможно, вы используете случай, когда написание дополнительного класса является подходящим решением?

person Alex    schedule 25.09.2015

Вы можете обернуть целое число в специальный класс и определить только setVar(), но не оператор присваивания, принимающий int

class M_type
{
    int m_var;
public:
    explicit M_type(int m) : m_var{m} {}
    operator int() const { return m_var; }
    void setVar(int a_var) { m_var = a_var; }
};

class foo 
{
    M_type m_var;
    bool m_isBig;
public:
    explicit foo(int m) : m_var{m} {};

    void method()
    {
        int copy = m_var; // OK, calls operator int()
        m_var = 5; // error, no operator=(int)
        m_var.setVar(101); // OK, calls setVar(int)
        doSomething();
    }

    void doSomething()
    {
        if(m_var > 5)
        { m_isBig = true; }
        else
        { m_isBig = false; }
    }    
};

Обязательно дайте M_type конструктор explicit, принимающий int, и только неявное преобразование operator int(), которое действует как "геттер". Если вы также сделаете конструктор неявным, сгенерированное компилятором присваивание operator=(M_type const&) сможет преобразовать аргумент 5 в M_type.

person TemplateRex    schedule 25.09.2015