我正在阅读我正在阅读的一本书中的C ++课程模板,虽然大部分内容都很明确,但这个特殊的功能真的让我烦恼:
template <typename T>
struct Vector3 {
T x; T y; T z;
//... several methods, constructors, etc
//Then this one, which is really confusing me:
template <typename P>
P* Write(P* pData)
{
Vector3<T>* pVector = (Vector3<T>*) pData;
*pVector++ = *this;
return (P*) pVector;
}
首先,这个函数似乎是处理一个P数组或一个指向P的指针,好像它可以很容易地从一个指向Vector3
(该类的名称)的指针进行处理。怎么样?如果我有一个Vector3<float> someVector
,那么指向这个someVector
castable的指针是指向float
的指针? (甚至int
?)它也是相反的:向函数提供一个浮点数组,然后它可以将它转换为Vector3
的数组。
这是我困惑的第一个方面。
接下来是*pVector++ = *this;
- 我认为这是指针算法,这样如果pVector
指向数组中的第二个元素,使用这个表达式,我们将指针递增到下一个数组的元素,但只有在之后我们首先将*this
分配给pVector
指向的当前元素。假设我在这方面是正确的,那么pVector
总是不会指向数组中的第一个元素,因为它只是在前一行创建的吗?!在这样的背景下++
运算符的目的是什么?
答案 0 :(得分:5)
这是函数声明。重要的一点是,P
与T
无关。因此,我们需要更多关注,因为可能会发生一些不好的事情......
template <typename P>
P* Write(P* pData)
{
让我们代表pData
指向的内存。为了便于解释,我假设pData
指向内存中足够大的区域(否则Write
可能会导致段错误),Vector3<T>
只是{大小为3 T
s。在下文中,我将T = float
与sizeof(float) = 4
一起使用,但我的解释仍然适用于其他类型。所以,sizeof(Vector3<float>) = 12
。
所以,这是内存,|-|
是一个字节:
|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|
^
|
pData : P*
我们将pData
解释为指向Vector3<T>
的指针。这通常是的错误样式,因为P
可以是任何东西。
Vector3<T>* pVector = (Vector3<T>*) pData;
以下一行:
*pVector++ = *this;
可分为:
*pVector = *this;
pVector++;
*pVector = *this;
将向量(*this
)的内容分配给数据。记忆现在是:
pVector : Vector3<float>*
|
v
|X|X|X|X|Y|Y|Y|Y|Z|Z|Z|Z|-|-|-|-|-|
^^^^^^^^^^^^^^^^^^^^^^^^^
copied content of the vector
pVector++;
将向量递增1 * sizeof(Vector3<float>)
,因此它现在指向尚未写入的内存:
pVector : Vector3<float>*
|
v
|X|X|X|X|Y|Y|Y|Y|Z|Z|Z|Z|-|-|-|-|-|
pVector
已投放回P*
并返回。
return (P*) pVector;
}
这使得链式写入成为可能,因为对返回指针的写入不会覆盖第一次写入:
char data[2 * sizeof(Vector3<float>)];
v2.Write(v1.Write(data));
// now data is:
// contents of v2
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// |X1|X1|X1|X1|Y1|Y1|Y1|Y1|Z1|Z1|Z1|Z1|X2|X2|X2|X2|Y2|Y2|Y2|Y2|Z2|Z2|Z2|Z2
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// contents of v1
此函数对io有用,相当于向量上的memcpy
。但是,它有很多缺陷,主要是模板参数。直接写入内存以进行此类操作应使用char
,而不是其他内容。在内存中写入以覆盖类的内容(即,不是io)是非常糟糕的实践,(赋值运算符是为这样的任务而制作的,它们更安全)。此外,以下示例不是自描述的:
vec.Write<MyTimerClass>(pointer); // Does not make sense if you are reading this line for the first time
其次,复制内存存在问题。在这里,在解释中,我假设Vector3
是一个简单的类型。情况并非总是如此。如果它具有虚函数和不同大小的成员,则内存中的布局将是实现定义的,例如:
X, Y, Z padding
------------------------ ----
|P|P|P|P|X|X|X|X|Y|Y|Y|Y|Z|Z|Z|Z|a|a|.|.
-------- ----
vtable some
pointer other
member
这对io来说不可靠。请参阅this SO question(接受的答案是:“以下是msvc的工作原理 ”......)。对于多个虚拟继承而言,它将成为一场真正的噩梦。
顺便说一句,这种方法的安全实施就像是:
template<typename IOBuffer>
bool Write(IOBuffer& buffer)
{
return buffer << X << Y << Z;
}
或更好:
virtual bool Write(IOBufferInterface& buffer)
{
return buffer << X << Y << Z;
}
理解,维护,调试等更简单......