const метод изменяет поле класса - ошибка проектирования?
От: Андрюха  
Дата: 17.06.11 01:27
Оценка:
Вот есть такой код

#include <iostream>
using namespace std;

class Foo {
public:

    Foo():i(0)    {}

    void foo(int &x) const
    {
        x++;
    }

    int i;
};


int main() {

    Foo obj;

    cout << "Before foo " << obj.i << endl;

    obj.foo(obj.i);

    cout << "After foo " << obj.i << endl;

    return 0;
}


Результат:
Before foo 0
After foo 1


Является ли ошибкой, то, что в моей программе требуется вызывать
obj.foo(obj.i);
?

Т.е. я константным методом изменяю свой объект.

Буду благодарен за любые комментарии и пожелания)
Re: const метод изменяет поле класса - ошибка проектирования
От: Centaur Россия  
Дата: 17.06.11 02:19
Оценка: 1 (1) +3
Здравствуйте, Андрюха, Вы писали:

А>Вот есть такой код


А>class Foo {
А>public:

А>    Foo():i(0)    {}

А>    void foo(int &x) const
А>    {
А>        x++;
А>    }

А>    int i;
А>};


Здесь ошибка проектирования в том, что член i публичный.
Re: const метод изменяет поле класса - ошибка проектирования
От: rg45 СССР  
Дата: 17.06.11 05:11
Оценка: +1
Здравствуйте, Андрюха, Вы писали:

А>Вот есть такой код

А>...
А>Является ли ошибкой, то, что в моей программе требуется вызывать
А>
А>obj.foo(obj.i);
А>
?

А>Т.е. я константным методом изменяю свой объект.

Для оценки дизайна, как минимум, нужно иметь представление о решаемой задаче и семантике класса. Из приведенного же тобой примера, как уже заметили, бросается в глаза только открытый доступ к переменной-члену i.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re: const метод изменяет поле класса - ошибка проектирования
От: carpenter Голландия  
Дата: 17.06.11 05:22
Оценка: +1
Здравствуйте, Андрюха, Вы писали:



А>Является ли ошибкой, то, что в моей программе требуется вызывать

А>
А>obj.foo(obj.i);
А>
?


А>Т.е. я константным методом изменяю свой объект.


стандарт цитировать не буду , ибо не имею ...
но полагаю компилятор не обязан проверять , что вы передаете в функцию
Весь мир — Кремль, а люди в нем — агенты
Re: const метод изменяет поле класса - ошибка проектирования
От: _niko_ Россия  
Дата: 17.06.11 05:30
Оценка:
Здравствуйте, Андрюха, Вы писали:

А>Вот есть такой код


А>
А>#include <iostream>
А>using namespace std;

А>class Foo {
А>public:

А>    Foo():i(0)    {}

А>    void foo(int &x) const
А>    {
А>        x++;
А>    }

А>    int i;
А>};


А>int main() {

А>    Foo obj;

А>    cout << "Before foo " << obj.i << endl;

А>    obj.foo(obj.i);

А>    cout << "After foo " << obj.i << endl;

А>    return 0;
А>}

А>


А>Результат:

А>
А>Before foo 0
А>After foo 1
А>


А>Является ли ошибкой, то, что в моей программе требуется вызывать

А>
А>obj.foo(obj.i);
А>
?


А>Т.е. я константным методом изменяю свой объект.


А>Буду благодарен за любые комментарии и пожелания)


Правильно ли я понял: у тебя проблема в том что тебе в константном методе необходимо как то изменить переменную класса и ты хочешь выкрутиться вот таким вот хитрым способом? Если да, то для таких целей есть mutable:


class A
{
public:
    A() : numberOfHits(0) { }

    Data getData() const
    {
        numberOfHits += 1;

        return Data();
    }
    . . . .
private:
    mutable int m_numberOfHits; // Колличество обращений
};
Re: const метод изменяет поле класса - ошибка проектирования
От: Erop Россия  
Дата: 17.06.11 05:30
Оценка:
Здравствуйте, Андрюха, Вы писали:

А>
А>#include <iostream>
А>using namespace std;

А>class Foo {
А>public:
А>    void foo(int &x) const
А>    {
А>        x++;
А>    }
А>    int i;
А>};
А>


1) Зачем Foo::foo не статический?
2) Зачем ему аргумент?

Вообще-то стремновато немного, но хорошо бы о семантике иметь представление. А то, если внутри foo есть какое-то хитрое выражение, использующее i, то при передаче ссылки на тот же i, в качестве параметра, можно получить всякие эффекты неожиданные, вплоть до UB...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: const метод изменяет поле класса - ошибка проектирования
От: Андрюха  
Дата: 17.06.11 08:08
Оценка:
Спасибо ответившим.

  • По публичному полю i. Я пытался упростить код, чтоб было легче заметить в чем суть вопроса. Ниже я привожу то, что ближе к решаемой задаче.
  • Про mutable я знаю. Передо мной не стоит задача измененить состояние объекта в константном методе.
  • Почему foo не статический, потому что это виртуальный метод (конечно из моего первого примера это не видно)))

    Вопрос возник только из-за того, что инспектируя метод foo, приходишь к выводу, что он должен быть константным.
    Когда смотришь на использование этого метода, то понимаешь, что даже если он и объявлен константным, то внутри его происходит изменение объекта...

    К стандарту или компилятору у меня претензий нет — все вроде бы как вполне логично.

    Просто интересно, как выйти из этой ситуации красиво?



    #include <iostream>
    using namespace std;
    
    class FooBase
    {
    public:
    
        FooBase():i(0), j(2){}
    
        void doSomething()
        {
            foo(this->i);
            cout << i << endl;
            int m = 8;
            foo(m);
            cout << m << endl;
        }
    protected:
        virtual void foo(int &x) const = 0;
        int j;
    private:
            int i;
    };
    
    class Foo: public FooBase {
    protected:
        void foo(int &x) const
        {
            x += j;
        }
    };
    
    
    int main() {
    
        Foo obj;
    
        obj.doSomething();
    
        return 0;
    }
  • Re[2]: const метод изменяет поле класса - ошибка проектирова
    От: _niko_ Россия  
    Дата: 17.06.11 08:30
    Оценка: 1 (1) +1
    Здравствуйте, Андрюха, Вы писали:

    . . . . .

    Если переменная что меняется является переменной внутренней для класса и используется только для его каких то нужд — вполне допустимо, ошибкой не назовут. Тут главное все задокументировать чтоб потом лишних вопросов не возникло
    Если же это данные на которые трудно "повесить" понятие временные — то тут велик шанс что закралась какая то ошибка при проектировании/реализации данного класса и наверное стоит что то пересмотреть/переделать.
    Re[2]: const метод изменяет поле класса - ошибка проектирова
    От: jerry_ru  
    Дата: 18.06.11 19:59
    Оценка: 1 (1)
    Здравствуйте, Андрюха

    Сделай для переменной i геттер : const int& get_i() const{ return i;}

    А потом решай новые проблеммы редизайна
    Re[3]: const метод изменяет поле класса - ошибка проектирова
    От: nen777w  
    Дата: 19.06.11 00:18
    Оценка:
    Здравствуйте, jerry_ru, Вы писали:

    _>Здравствуйте, Андрюха


    _>Сделай для переменной i геттер : const int& get_i() const{ return i;}


    _>А потом решай новые проблеммы редизайна


    А какой смысл возвращать по константной ссылке переменную интегрального типа?
    Re[4]: const метод изменяет поле класса - ошибка проектирова
    От: Андрюха  
    Дата: 19.06.11 01:29
    Оценка:
    Здравствуйте, nen777w, Вы писали:

    N>А какой смысл возвращать по константной ссылке переменную интегрального типа?


    В моем случае i может быть не интегрального типа. int используется для простоты иллюстрации.
    Re[2]: const-метод изменяет поле класса - ошибка проектирова
    От: Roman Odaisky Украина  
    Дата: 19.06.11 07:58
    Оценка:
    А>внутри его происходит изменение объекта...

    Не «внутри foo», а «внутри doSomething». Этот и объявляй const, тогда изменения не будет.
    До последнего не верил в пирамиду Лебедева.
    Re[2]: const метод изменяет поле класса - ошибка проектирова
    От: Erop Россия  
    Дата: 20.06.11 08:22
    Оценка: 1 (1)
    Здравствуйте, Андрюха, Вы писали:

    А>Спасибо ответившим.

    Для "спасибо" тут есть кнопки

    А>
  • Почему foo не статический, потому что это виртуальный метод (конечно из моего первого примера это не видно)))
    Ага! Это меняет дело!
    А тогда ещё один вопрос. Нужно ли чтобы класс-база, этих всех виртуальных действий, был бы ещё и тем самым классом, у которого есть поле i?


    А>
    А>    void doSomething()
    А>    {
    А>        foo(this->i);
    А>        cout << i << endl;
    А>        int m = 8;
    А>        foo(m);
    А>        cout << m << endl;
    А>    }
    А>    void foo(int &x) const
    А>    {
    А>        x += j;
    А>    }
    А>}
    А>


    1) Нехорошо то, что если передать в foo, не i, а j, и выражение в foo будет позаборестее, то возможны всякие эффекты.
    Я бы от этого таки как-то защитился бы. Например так:
    assert( &x != &j );

    2) Ничего гиперужасного в этом примере я лично не вижу, но возникает вопрос. А точно ли надо так хитро передавать поле для заполнения?
    Если бы foo выглядела как
    int foo( int );

    и использовалась бы
    this->j = this->foo( this-> j );

    то вопросов бы и не возникало никаких... Кроме того, тогда принципиально нельзя бы было накрячится, передав в foo не тот объект и пункта (1) этого сообщения бы не было бы.
    Точно ли этот тип так дорог в копировании?
    3) Ещё, кстати есть опасность срезки всякой. Если тип i, на самом деле какой-то кудрявый и со всякими наследниками и полиморфизмом, то всё меняется.
    Но, сначала, было бы хорошо понять, хотя бы, что это за тип?
    Ещё лучше рассказать задачу ещё более полно/понятно. IMHO.

    Я вот попробовал представить себе, зачем это могло бы быть надо. Но пока что ничего умнее читалки из файла сложного формата не придумал.
    Но такую машинку я бы, конечно, делал не так. Я бы сделал отдельно читалку, вовсе не полиморфную, а отдельно разборщик формата, полиморфный. И имел бы на него указатель в читалке.
    Так что хорошо бы понять что на самом деле тебе надо, тогда будет проще дать дельный совет.
  • Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
    Re[4]: const метод изменяет поле класса - ошибка проектирова
    От: _nn_ www.nemerleweb.com
    Дата: 20.06.11 09:44
    Оценка:
    Здравствуйте, nen777w, Вы писали:

    N>Здравствуйте, jerry_ru, Вы писали:


    _>>Здравствуйте, Андрюха


    _>>Сделай для переменной i геттер : const int& get_i() const{ return i;}


    _>>А потом решай новые проблеммы редизайна


    N>А какой смысл возвращать по константной ссылке переменную интегрального типа?


    Чтобы можно было писать
    &get_i()

    P.S.
    Практического смысла конечно немного.
    http://rsdn.nemerleweb.com
    http://nemerleweb.com
    Re: const метод изменяет поле класса - ошибка проектирования
    От: MasterZiv СССР  
    Дата: 29.06.11 06:29
    Оценка:
    On 17.06.2011 5:27, Андрюха wrote:

    > class Foo {

    > public:
    >
    > Foo():i(0) {}
    >
    > void foo(int &x)const
    > {
    > x++;
    > }

    // метод не меняет содержимое объекта, const указан правильно, ошибки нет.

    >

    > int i;
    > };
    >
    >
    > int main() {
    >
    > Foo obj;
    >
    > cout<< "Before foo" << obj.i<< endl;
    >
    > obj.foo(obj.i);
    >
    > cout<< "After foo" << obj.i<< endl;
    >
    > return 0;
    > }

    > Является ли ошибкой, то, что в моей программе требуется вызывать

    >
    > obj.foo(obj.i);
    >
    > ?
    >
    > Т.е. я константным методом изменяю свой объект.

    Нет, не является. Метод foo у тебя меняет не свой объект, а int, переданный
    в него по ссылке через параметр. То, что этим int-ом оказался мембер
    того же объекта Foo -- это ответственность не метода Foo::foo, а вызывающего
    этот метод кода.

    То, что в твоей программе НУЖНО вызывать obj.foo(obj.i) далеко не очевидно
    из приведённого кода, и не понятно, собственно, почему оно нужно, и почему
    бы это было плохо. Метод Foo::foo законно константный, законно не меняет
    ничего в Foo и законно меняет переданный по ссылке int.
    А вот что он не использует вообще ничего из объекта Foo -- странно,
    и если это не упрощение, сделанное для наглядности примера, то это -- ошибка
    проектирования, в таком случае должно быть так:

    static void foo(int &x);
    Posted via RSDN NNTP Server 2.1 beta
    Re[2]: const метод изменяет поле класса - ошибка проектирова
    От: MasterZiv СССР  
    Дата: 29.06.11 06:30
    Оценка:
    On 17.06.2011 9:22, carpenter wrote:

    > но полагаю компилятор не обязан проверять , *что* вы передаете в функцию


    Не обязан однозначно.
    Posted via RSDN NNTP Server 2.1 beta
    Re[2]: const метод изменяет поле класса - ошибка проектирова
    От: MasterZiv СССР  
    Дата: 29.06.11 06:35
    Оценка: 1 (1)
    On 17.06.2011 12:08, Андрюха wrote:

    > # Почему foo не статический, потому что это виртуальный метод (конечно из моего

    > первого примера это не видно)))

    А вот ЭТО уже может быть ошибкой проектирования. Виртуальными должны быть
    методы, которые делают что-то С ОБЪЕКТОМ (this), а не с внешними данными.
    Это конечно нестрого, но это просто как-то странно.
    Лучше применять что-то типа стратегий или визиторов.

    > Вопрос возник только из-за того, что инспектируя метод foo, приходишь к выводу,

    > что он должен быть константным.
    > Когда смотришь на использование этого метода, то понимаешь, что даже если он
    > и объявлен константным, то внутри его происходит изменение объекта...

    Вообще-то, если он виртуальный, его сигнатура вместе с const уже фиксирована в
    родительском классе. Если ты добавиш const, этот метод перестанет быть
    реализацией виртуального класса из базового класса и станет просто перегруженным
    невиртуальным методом с таким же названием и другой сигнатурой.
    Posted via RSDN NNTP Server 2.1 beta
    Re[4]: const метод изменяет поле класса - ошибка проектирова
    От: MasterZiv СССР  
    Дата: 29.06.11 06:38
    Оценка:
    On 19.06.2011 4:18, nen777w wrote:
    > А какой смысл возвращать по константной ссылке переменную интегрального типа?

    А ты попробуй ответить для себя на другой вопрос:
    "Какой смысл НЕ возвращать по константной ссылке переменную интегрального типа?"

    Например -- для унификации интерфейса.
    Posted via RSDN NNTP Server 2.1 beta
     
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.