以下简化(可编译)示例说明了可能的切片分配方案。
#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*
?
免责声明:我正在回答问题,但如果您想知道如何实现这一点,那么您的设计可能出现问题的可能性很大。
简短的回答:这不能用公共继承来完成,完全停止。公共继承的全部意义在于,无论上下文如何,指向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;
}