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-aBase
。