Извикване на константна функция от друг обект на клас

Ето кода, който имам

class A
{
public:
    void Func1() const;
};

class B
{
public:
    A* a;
    void Func2() const
    {
        // do something with 'a'
    }
};

void A::Func1() const
{
    B b;
    b.a = this;
    b.Func2();
}

Сега очевидно това ми дава грешка в реда, защото се опитвам да конвертирам от const в non-const.

b.a = this;

Има ли някакъв начин да извикате Func2, без да се налага да отхвърляте постоянството на this. Тъй като Func2 така или иначе е постоянна функция, тя няма да промени this.


person user1353535    schedule 17.05.2012    source източник
comment
Не можете да промените нищо общо с обекта, когато имате функция const.. Или я наречете const *A a, или премахнете const от функцията.   -  person Martol1ni    schedule 17.05.2012


Отговори (4)


Ако class B винаги ще работи с *a като const обект, тогава, както казаха други, всичко, което е необходимо, е просто да промените декларацията на

public: const A* a

Тук трябва да спомена, че постоянството на B::Func2 е червена херинга, защото няма абсолютно никаква връзка с постоянството на B::a. Това B::Func2 е const означава, че не е разрешено да се променя стойността на a; въпреки това е разрешено да дереферира a и да променя получения обект.

Сега, ако class B има както const, така и не-const операции по отношение на *a, тогава дизайнът на вашия клас трябва да се промени. Би било много по-добре, ако смените class B да използвате const A* a както по-горе, и добавите друг class D : public B, който капсулира всички мутиращи операции. В допълнение, a трябва да бъде скрит зад настройка на свойства; това ви позволява да правите неща като

class B {
    const A* a;
public:
    void setA(const A* a) { this->a = a; }
    void Func2() const {}
};

class D : public B {
    A* a;
public:
    using B::setA;
    void setA(A* a) { 
        this->a = a;
        static_cast<B*>(this)->setA(const_cast<const A*>(a));
    }
    void Func3() { /* do something to D::a */ }
};

С тази схема както B, така и D поддържат независими, подходящо въведени указатели към обекта, до който ще се осъществява достъп. Ако setA се извика на B или на D с параметър const A*, тогава се задава само B::a. Ако setA се извика на D с A*, тогава и B::a, и D::a са правилно зададени. Това стана възможно, защото чрез абстрахиране на члена зад сетер можете да претоварите сетера относно постоянството на неговия параметър.

person Jon    schedule 17.05.2012

Трябва да декларирате A* като const:

class A
{
public:
    void Func1() const;
};

class B
{
public:
    const A* a;
    void Func2() const
    {
        // do something with 'a'
    }
};

void A::Func1() const
{
    B b;
    b.a = this;
    b.Func2();
}

Тъй като в A::Func1, указателят this е const.

person mfontanini    schedule 17.05.2012
comment
Това решава проблема, но не мога да имам 'a' като const, защото B има други функции, които могат да променят 'a'. Знам, че има грешка в моя дизайн, бихте ли предложили по-добър. - person user1353535; 17.05.2012

Func2 може да не промени this, но b.a не е const и вие сте свободни да го промените след това. Няма правилен начин да направите това, въпреки че съществуват заобиколни решения, като mutable или const_cast.

Това е знак за грешен дизайн.

person Luchian Grigore    schedule 17.05.2012

Да, направете A * константа:

class B {
public:
    const A *a

...
};
person Richard J. Ross III    schedule 17.05.2012