此常见问题解答涉及聚合和POD,并涵盖以下材料:
考虑这个例子,其中基类有一些数据成员,而派生类只提供了一个额外的方法:
struct TestBase
{
int x;
TestBase() : x(5) {}
};
struct TestDerived : public TestBase
{
void myMethod()
{
x=8;
}
};
int main()
{
TestBase b;
TestDerived& d=static_cast<TestDerived&>(b);
d.myMethod();
}
Run Code Online (Sandbox Code Playgroud)
这是向下错误的类型,所以AFAIU它有未定义的行为.但是这样的情况可能会有一些例外吗,派生类的布局与基类的布局相同?
(我编辑了这个问题是为了避免干扰.在任何其他问题有意义之前,有一个核心问题需要澄清.对现在的答案似乎不太重要的任何人道歉.)
让我们建立一个具体的例子:
struct Base {
int i;
};
Run Code Online (Sandbox Code Playgroud)
没有虚方法,也没有继承,通常是一个非常愚蠢和简单的对象.因此,它是普通旧数据(POD),它可以回溯到可预测的布局.特别是:
Base b;
&b == reinterpret_cast<B*>&(b.i);
Run Code Online (Sandbox Code Playgroud)
这是根据维基百科(它本身声称参考C++ 03标准):
指向一个POD结构对象,适宜地转换使用重新解释铸造,点到它的初始构件,反之亦然,这意味着有在POD结构的开头没有填充.[8]
现在让我们考虑继承:
struct Derived : public Base {
};
Run Code Online (Sandbox Code Playgroud)
同样,没有虚方法,没有虚继承,也没有多重继承.因此这也是POD.
问题:这个事实(Derived是C++ 11中的POD)是否允许我们这样说:
Derived d;
&d == reinterpret_cast<D*>&(d.i); // true on g++-4.6
Run Code Online (Sandbox Code Playgroud)
如果这是真的,那么以下将是明确定义的:
Base *b = reinterpret_cast<Base*>(malloc(sizeof(Derived)));
free(b); // It will be freeing the same address, so this is OK
Run Code Online (Sandbox Code Playgroud)
我不会在这里问new及delete- 这更容易考虑malloc和free.在这种简单的情况下,我只是对有关派生对象布局的规定感到好奇,并且基类的初始非静态成员位于可预测的位置.
Derived对象应该等效于:
struct Derived { // no inheritance
Base b; // …Run Code Online (Sandbox Code Playgroud) 我写了很多处理消息协议的代码。消息协议通常会具有通用的消息帧,可以从串行端口或套接字反序列化。该帧包含消息类型,并且必须根据消息类型来处理消息有效负载。
通常,我用访问器方法和构造函数编写一个多态类集,该构造函数引用消息框。
我想到的是,我可以直接从消息框架派生访问器类,然后从消息框架重新解释_cast到适当的访问器类,而不是基于对消息框架的引用来构造访问器类。这使代码更加简洁,并节省了一些字节和处理器周期。
请参见下面的示例(极度虚构和压缩)。显然,对于生产代码,所有这些都需要适当地封装,强制转换成为派生类的成员,更好地分离了所关注的问题,并添加了一些验证。为了简洁起见,已全部删除。
#include <iostream>
#include <cstring>
#include <vector>
struct GenericMessage
{
GenericMessage(const char* body):body_(body, body+strlen(body)){}
std::vector<char> body_;
};
struct MessageType1:public GenericMessage
{
int GetFoo()const
{
return body_[2];
}
int GetBar()const
{
return body_[3];
}
};
int main()
{
GenericMessage myGenericMessage("1234");
MessageType1* myMgessageType1 = reinterpret_cast<MessageType1*>(&myGenericMessage);
std::cout << "Foo:" << myMgessageType1->GetFoo() << std::endl;
std::cout << "Bar:" << myMgessageType1->GetBar() << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我从来没有见过任何地方这样做。考虑到派生没有其他数据成员,以这种方式从基础到派生有任何不利之处吗?