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

C++ C++;:如何防止通过指向派生对象基子对象的指针修改派生对象?,c++,inheritance,constants,implicit-conversion,type-safety,C++,Inheritance,Constants,Implicit Conversion,Type Safety,下面的简化(但可编译)示例说明了一种可能的切片分配场景 #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();

下面的简化(但可编译)示例说明了一种可能的切片分配场景

#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;
}
#包括
结构基
{
//变异的方法。不可能使它虚拟化。
模板
基本运算符和运算符=(常量任意项和x)
{
m_int=x.AsInteger();
归还*这个;
}
int AsInteger()常量
{
返回m_int;
}
int m_int;
};
结构派生:公共基
{
模板
派生运算符=(常量Anything&x)
{
m_text=x.AsString();
Base::operator=(x);
归还*这个;
}
常量std::string&AsString()常量
{
返回m_文本;
}
//不变量:派生::m_文本与基::m_x匹配。
std::字符串m_文本;
};
无效检查基(常数基*b)
{
b->AsInteger();
}
无效检查库(常数基和b)
{
b、 AsInteger();
}
void InitBase(Base*b)
{
*b=基();
}
void InitBase(Base&b)
{
b=基();
}
int main()
{
碱基b;

InitBase(b);//免责声明:我正在回答问题,但如果您想知道如何实现这一点,那么很可能您的设计有问题

简短回答:这不能用公共继承来完成,句号。公共继承的全部要点是,指向
派生的对象的引用或指针可以用作指向
基的对象的引用或指针,而不管上下文如何

因此,实现这一点的方法是通过私有继承或成员变量,只通过返回
const
引用或指针的访问器公开
Base
成员:

#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;
}
#包括
结构基
{
//变异的方法。不可能使它虚拟化。
模板
基本运算符和运算符=(常量任意项和x)
{
m_int=x.AsInteger();
归还*这个;
}
int AsInteger()常量
{
返回m_int;
}
int m_int;
};
派生结构:私有基
{
模板
派生运算符=(常量Anything&x)
{
m_text=x.AsString();
Base::operator=(x);
归还*这个;
}
常量std::string&AsString()常量
{
返回m_文本;
}
const Base&Base()const{return*this;}
//不变量:派生::m_文本与基::m_x匹配。
std::字符串m_文本;
};
无效检查基(常数基*b)
{
b->AsInteger();
}
无效检查库(常数基和b)
{
b、 AsInteger();
}
void InitBase(Base*b)
{
*b=基();
}
void InitBase(Base&b)
{
b=基();
}
int main()
{
碱基b;

InitBase(b);//这并不是对您的具体问题的回答,但听起来您的设计有这样一个要求,非常不确定。继承的全部要点是您想要一个“is-a”关系。我的直觉是,
Base
应该是
Derived
的成员。很抱歉没有在问题中提到这一方面……是的,你是对的。继承可能不是这种关系的正确选择。我只想允许在非变异访问
Base的所有地方使用
Derived
就足够了。为了给你画一幅图,让我们想象一下,
Base
是一个非拥有的访问器,就像
std::span
派生的
是拥有容器家族中的某个类。虚拟副本分配操作符可能会起作用。@NathanOliver会阻止指针向上投射吗?@Frank不会阻止投射但是它应该使
b=Base();
调用
派生的
操作符=
,因为它实际上就是这样的。谢谢你的建议,弗兰克,但是需要显式调用转换函数(即
Base()
)这正是我希望避免的…我希望在任何适合
常量Base
的地方都使用
派生
。这就是为什么我从“is-a”关系开始:
派生
在每个非变异上下文中都使用is-a
Base