我正在研究我正在阅读的一本书中的 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
可转换为指向 float
的指针? (甚至 int
?)它也做相反的事情:为函数提供一个 float 组,然后它可以将它转换为 Vector3
的数组。 .
所以这是我感到困惑的第一个方面。
接下来是 *pVector++ = *this;
-- 我认为这是指针算法,如果 pVector
指向数组中的第二个元素,使用此表达式,我们将指针递增到数组的下一个元素,但只有 after 我们首先分配 *this
pVector
指向的当前元素.假设我在这方面是正确的,不会 pVector
总是指向数组中的第一个元素,因为它只是在上一行创建的?! ++
的目的是什么?在这种情况下的运营商?
最佳答案
让我们分解一下它是如何工作的:
这是函数声明。重要的一点是
P
与T
无关.所以,我们需要多加注意,因为可能会发生一些不好的事情......template <typename P> P* Write(P* pData) {
让我们代表
pData
指向的内存.为了便于解释,我假设pData
指向内存中足够大的区域(否则Write
可能会导致段错误),并且Vector3<T>
只有 3T
的大小秒。下面我取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;
将 vector (*this
) 的内容分配给数据。现在的内存是:pVector : Vector3<float>* | v |X|X|X|X|Y|Y|Y|Y|Z|Z|Z|Z|-|-|-|-|-| ^^^^^^^^^^^^^^^^^^^^^^^^^ copied content of the vector
pVector++;
将 vector 递增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
在 vector 上。然而,它有很多缺陷,主要的一个是模板参数。直接写入内存进行此类操作应使用 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 (公认的答案是:“这是 for msvc 的工作方式”...)。对于多重虚拟继承,它变成了真正的噩梦。
顺便说一句,这种方法的安全实现应该是这样的:
template<typename IOBuffer>
bool Write(IOBuffer& buffer)
{
return buffer << X << Y << Z;
}
或更好:
virtual bool Write(IOBufferInterface& buffer)
{
return buffer << X << Y << Z;
}
它更易于理解、维护、调试等...
关于c++ - 这个类模板中的这个函数模板是干什么的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15999363/