C ++:如何通过指向其基础子对象的指针阻止对派生对象的修改?

问题描述 投票:2回答:1

以下简化(可编译)示例说明了可能的切片分配方案。

#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之间的一致性。

我有兴趣防止这种切片修改发生,但保留了行(1),(2),(3)和(4)的有效性。

所以问题是:

a)是否有任何技巧可能会阻止通过指向派生类的指针调用基类的非const成员函数?

b)是否有任何技巧可以阻止从Derived*Base*的标准隐式转换,但仍允许从Derived*转换为const Base*

c++ inheritance const implicit-conversion type-safety
1个回答
2
投票

免责声明:我正在回答问题,但如果您想知道如何实现这一点,那么您的设计可能出现问题的可能性很大。

简短的回答:这不能用公共继承来完成,完全停止。公共继承的全部意义在于,无论上下文如何,指向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;
}
© www.soinside.com 2019 - 2024. All rights reserved.