这个类模板中的这个函数模板是做什么的?

时间:2013-04-14 13:06:09

标签: c++

我正在阅读我正在阅读的一本书中的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总是不会指向数组中的第一个元素,因为它只是在前一行创建的吗?!在这样的背景下++运算符的目的是什么?

1 个答案:

答案 0 :(得分:5)

让我们分解它的工作原理:

  1. 这是函数声明。重要的一点是,PT无关。因此,我们需要更多关注,因为可能会发生一些不好的事情......

    template <typename P> 
    P* Write(P* pData)
    {
    

    让我们代表pData指向的内存。为了便于解释,我假设pData指向内存中足够大的区域(否则Write可能会导致段错误),Vector3<T>只是{大小为3 T s。在下文中,我将T = floatsizeof(float) = 4一起使用,但我的解释仍然适用于其他类型。所以,sizeof(Vector3<float>) = 12

    所以,这是内存,|-|是一个字节:

    |-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|
    ^
    |
    pData : P*
    
  2. 我们将pData解释为指向Vector3<T>的指针。这通常是的错误样式,因为P可以是任何东西。

        Vector3<T>* pVector = (Vector3<T>*) pData; 
    
  3. 以下一行:

        *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|-|-|-|-|-|
      
  4. 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
    
  5. 现在,一些缺陷:

    此函数对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;
    }
    

    理解,维护,调试等更简单......