编辑:完全重述的问题:回答如下:
将派生类型作为对基类型的引用传递时遇到问题。似乎引用的对象在从 Derived 转换为 Base 之前/之后清除/重新初始化了它的数据。
我认为当将 Derived 实例传递给接受参数作为对 Base 对象(Derived -> Base&)的引用的函数时,引用的 Base 实例将在内存中与 Derived 对象保持一致,除非作为值传递。
我有 Base 类,一种被认为是“只读”的数据状态:
class ReadonlyText
{
protected:
int m_Length;
char* m_String;
public:
ReadonlyText() m_Length(0), m_String(0) {} // a null base
ReadonlyText(const char* str) : m_Length(0), m_String(str)
{
do
{
m_Length++;
}while(*ptr++);
}
ReadonlyText(const ReadonlyText& copy) : m_Length(copy.m_Length), m_String(copy.m_String)
{
}
virtual ~Readonly() {};
... virtual nonsense
}; // lets not forget the semicolon
现在我有了 Derived 类,它是可写的:
class Text
{
public:
Text() : m_Length(1), m_String(Memory::New<char>(m_Length)) {}
Text(const ReadonlyText& copy) : m_Length(copy.m_Length), m_String(Memory::New<char>(m_Length))
{
for(whole i = 0; i < m_Length; ++i)
m_String[i] = copy.m_String[i];
}
Text(const Text& copy) : m_Length(copy.m_Length), m_String(Memory::New<char>(m_Length))
{
for(whole i = 0; i < m_Length; ++i)
m_String[i] = copy.m_String[i];
}
virtual ~Text()
{
if((m_Length != 0 && m_String != null<char>::value))
{
Memory::Delete<Char>(m_String);
m_Length = 0;
m_String = null<Char>::value;
}
}
// operator==(ReadonlyText&)
// operator!=(ReadonlyText&)
// operator==(Text&)
// operator!=(Text&)
// operator=(Text&)
// operator=(ReadonlyText&)
// I don't have time to format all these functions for best visibility in SO,
// I assure you that all these functions are implemented properly
// THE PROBLEM occurs in any function with ReadonlyText&, say:
Text operator+(const ReadonlyText& rhs)
{
//...
// Before the function begins, 'rhs.m_String' == 0x0
// see case below
}
};
// ...
int main(...)
{
Text t1 = Text("hello");
Text t2 = Text("world");
// in the debugger, at this point, both t1 && t2 '.m_String' is valid
// as "hello" and "world" respectively
// but when stepping into:
Text t3 = t1 + t2; // the function described above...
// ...which is t1.operator+(t2) // where t2 is passed as ReadonlyText&
// the debugger shows:
// t1.m_String == "hello"
// t2.m_String == 0x0 -- this should be "world"
// since no copy construction is occuring, (passing as reference), then
// the data should be consistent with the derived type, right?
}
发生的情况是,在 operator+ 中,我尝试访问“rhs.m_String”,它应该被评估为“world”,但由于某种原因已被实例化为新的 ReadonlyText 而不是作为参考传递?
此外,该程序不会因任何消息而崩溃;没有分段错误或错误。相反,它只是完全退出,好像应用程序运行顺利,直到 main 结束?
没有比我在这里描述的更多的事情发生了。Memory::New 和 Memory::Delete 封装了内存操作(new 和 delete),以便 New 根据模板类型名的大小和给定的长度返回一个指针。删除只是确保正确销毁指针。null 是一种结构,可确保类型名称的一致空值:
//class Memory:
template<typename T> static T* New(whole length = 1)
{
return (T*)(operator new (length * sizeof(T)));
}
template<typename T> static void Delete(T* pointer)
{
operator delete (pointer);
}
// null struct
template<typename T>
struct null
{
static constexpr T* value = 0x0;
}
真正的问题:
<_< 在重新设计类层次结构时,我忘记删除派生成员(m_Length 和 m_String)。因此 Derived 实例具有屏蔽 Base 成员的成员。长话短说,当某些事情没有意义时,可能是你的错:
class Base
{
...
protected:
int m_Length;
char* m_String;
};
class Derived : public virtual Base
{
...
protected: // xD WTH was I hoping to do here???
int m_Length;
char* m_String;
};
应该有一个用于继承的编译器功能可以防止屏蔽,但在此之前要小心……只是要小心。感谢所有尝试帮助我的人,以及那些退出的人。