C ++: Как предотвратить изменение производного объекта через указатель на его базовый подобъект? - PullRequest
0 голосов
/ 22 февраля 2019

Следующий упрощенный (но все же компилируемый) пример иллюстрирует возможный сценарий назначения срезов.

#include <string>

struct Base
{
    // Mutating method. Not a chance of making it virtual.
    template <typename Anything>
    Base& operator=(const Anything& x)
    {
        m_int = x.AsInteger();
        return *this;
    }

    int AsInteger() const
    {
        return m_int;
    }

    int  m_int;
};

struct Derived : public Base
{
    template <typename Anything>
    Derived& operator=(const Anything& x)
    {
        m_text = x.AsString();
        Base::operator=(x);
        return *this;
    }
    const std::string& AsString() const
    {
        return m_text;
    }

    // Invariant: Derived::m_text matches Base::m_x.
    std::string   m_text;
};

void ExamineBase(const Base* b)
{
    b->AsInteger();
}

void ExamineBase(const Base& b)
{
    b.AsInteger();
}

void InitBase(Base* b)
{
    *b = Base();
}

void InitBase(Base& b)
{
    b = Base();
}


int main()
{
    Base           b;
    InitBase(b);          // <----- (1)
    InitBase(&b);         // <----- (2)

    Derived        d;
    Derived&       ref = d;
    Derived*       ptr = &d;

    ExamineBase(ref);     // <----- (3)
    ExamineBase(ptr);     // <----- (4)

    InitBase(ref);        // <----- (5)
    InitBase(ptr);        // <----- (6)

    return 0;
}

Строки (1), (2), (3) и (4) хороши.

Строки (5) и (6) демонстрируют проблему: они изменяют только базовый подобъект внутри законченного объекта, очевидно нарушая согласованность между Base :: m_int и Derived :: m_text.

I'mзаинтересованы в предотвращении такой модификации среза, сохраняя при этом достоверность строк (1), (2), (3) и (4).

Итак, вопросы:

aЕсть ли какие-нибудь приемы, которые могут помешать вызову неконстантных функций-членов базового класса через указатель на производный класс?

b) Существуют ли приемы, которые могут блокировать стандартное неявное преобразование из Derived* в Base*, но все же разрешить преобразование из Derived* в const Base*?

1 Ответ

0 голосов
/ 22 февраля 2019

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

ShortОтвет: это не может быть сделано с публичным наследством, полная остановка.Весь смысл общественного наследования заключается в том, что референс или указатель на объект Derived можно использовать как ссылку или указатель на объект Base, независимо от контекста.

Итак, путь ксделать это было бы, чтобы пройти через частное наследование или переменную-член, и выставить член Base только через метод доступа, возвращающий ссылку или указатель const:

#include <string>
struct Base
{
    // Mutating method. Not a chance of making it virtual.
    template <typename Anything>
    Base& operator=(const Anything& x)
    {
        m_int = x.AsInteger();
        return *this;
    }

    int AsInteger() const
    {
        return m_int;
    }

    int  m_int;
};

struct Derived : private Base
{
    template <typename Anything>
    Derived& operator=(const Anything& x)
    {
        m_text = x.AsString();
        Base::operator=(x);
        return *this;
    }
    const std::string& AsString() const
    {
        return m_text;
    }

    const Base& base() const {return *this;}

    // Invariant: Derived::m_text matches Base::m_x.
    std::string   m_text;
};

void ExamineBase(const Base* b)
{
    b->AsInteger();
}

void ExamineBase(const Base& b)
{
    b.AsInteger();
}

void InitBase(Base* b)
{
    *b = Base();
}

void InitBase(Base& b)
{
    b = Base();
}


int main()
{
    Base           b;
    InitBase(b);          // <----- (1)
    InitBase(&b);         // <----- (2)

    Derived        d;
    Derived&       ref = d;
    Derived*       ptr = &d;

    ExamineBase(ref.base());     // <----- (3)
    ExamineBase(&ptr->base());     // <----- (4)

    InitBase(ref.base());        // <----- BOOM!
    InitBase(&ptr->base());        // <----- BOOM!

    return 0;
}
...