3

我正在尝试了解有关模板的更多信息,但遇到了一个我似乎无法解决的问题。目前,下面的课程工作正常。

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;

template <class T, int s>
class myArray{
public:
    T* data;
    inline T& operator[](const int i){return data[i];}
    myArray(){
        data=new T[s];
    }
    myArray(const myArray& other){
        data=new T[s];
        copy(other.data,other.data+s,data);
    }
    myArray& operator=(const myArray& other){
        data=new T[s];
        copy(other.data,other.data+s,data);
        return *this;
    }
    ~myArray(){delete [] data;}
};  

如果我使用它:

myArray<myArray<myArray<int,10>,20>,30> a;

a 现在是 30x20x10 数组,我可以使用普通数组括号访问它,例如 a[5][5][5]。我希望添加一个功能,以便我可以编写:

myArray<myArray<myArray<int,10>,20>,30> a(10);

例如,将所有条目初始化为 10。我不知道该怎么做。据我了解, myArray 的每一层都是使用默认构造函数构造的。如果我将其更改为:

myArray(int n=0){
        data=new T[s]; 
        fill(data,data+s,n); //T might not be of type int so this could fail.
}

我认为当数据不是 int 类型(即在维度> 1 的任何数组上)时,这应该会失败,但事实并非如此。它在数组为正方形时有效,但如果不是,则某些条目未设置为 10。有谁知道标准向量类如何实现这一点?任何帮助都会很棒。谢谢!

4

3 回答 3

3

好吧,尝试这样的事情:

 myArray()
 : data(new T[s]())      // value-initialization!
 {
 }

 myArray(T const & val)
 : data(new T[s])        // default-initialization suffices
 {
     std::fill(data, data + s, val);
 }

如果你喜欢可变参数模板,你可能会编造一些更怪诞的东西,涉及可变参数填充的初始化列表,但我认为我们已经完成了一周的学习。

请注意 using 的基本缺陷new:任何一个版本都要求您的类T可以在某些“默认”状态下实例化,并且它是可分配的,即使我们在第二个版本中从不要求默认状态。这就是为什么“真正的”库将内存分配和对象构造分开的原因,new除非是放置版本,否则您永远不会看到表达式。

于 2012-11-05T16:39:40.007 回答
1

对包含其他数组的数组进行专门化。为此,您需要一些通用的实现类来通用和专门的 MyArray:

常见实现(我为您做了一些修复 - 请参阅 !!! 评论):

template <class T, int s>
class myArrayImpl {
public:
    T* data;
    T& operator[](int i){return data[i];} //!!! const before int not needed
    const T& operator[](int i) const {return data[i];} //!!! was missing
    myArrayImpl(){
        data=new T[s]();
    }
    myArrayImpl(const myArrayImpl & other){
        data=new T[s];
        copy(other.data,other.data+s,data);
    }
    myArrayImpl& operator=(const myArrayImpl& other){
        T* olddata = data; // !!! need to store old data
        data=new T[s];
        copy(other.data,other.data+s,data);
        delete [] olddata; //!!! to delete it after copying
        return *this;
    }
    ~myArrayImpl(){delete [] data;}
};  

然后进行一般实现 - 注意value_typeand的定义setAll

template <class T, int s>
class myArray : private myArrayImpl<T,s> {
    typedef myArrayImpl<T,s> Impl;
public:
    using Impl::operator[];
    myArray() : Impl() {}
    typedef T value_type; // !!!
    explicit myArray(const value_type& value) {
       setAll(value);
    }
    void setAll(const value_type& value) {
       fill(this->data, this->data + s, value);
    }
};

以及 myArray 的 myArray 的专用版本 - 另请参见value_type和的区别setAll

template <class T, int s1, int s2>
class myArray<myArray<T,s2>,s1> : private myArrayImpl<myArray<T,s2>,s1> {
    typedef myArrayImpl<myArray<T,s2>,s1> Impl;
public:
    using Impl::operator[];
    myArray() : Impl() {}
    typedef typename myArray<T,s2>::value_type value_type; // !!!
    explicit myArray(const value_type& value) {
       setAll(value);
    }
    void setAll(const value_type& value) {
       for_each(this->data, this->data + s1, [value](myArray<T,s2>& v) { v.setAll(value); });
    }
};  

和用法:

int main() {
  myArray<myArray<myArray<int,7>,8>,9> a(7);
  std::cout << a[0][0][0] << std::endl;
  std::cout << a[8][7][6] << std::endl;
}

完整示例:http: //ideone.com/0wdT9D

于 2012-11-05T17:36:58.920 回答
1

std::vector 在内存块上使用新位置。它在第二行代码中分配内存后构造数据。

这种技术也适用于你。小心放置 new 因为它也需要您手动调用析构函数。

这是一条没有放置新的半途而废的路线:

template<typename U>
explicit MyArray( U const& constructFromAnythingElse )
{
  AllocateSpace(N); // write this, just allocates space
  for (int i = 0; i < N; ++i)
  {
    Element(i) = T( constructFromAnythingElse );
  }
}

使用placement new,你必须先分配内存,然后就地构造,最后记得销毁每个元素。

与placement new route相比,上述内容是半途而废的,因为我们首先构造每个元素,然后再构建另一个元素,并使用operator=它来覆盖它。

通过使其成为任意类型的模板构造函数,我们不依赖于多次转换来获得多个级别到数组中。朴素的版本(你取一个 T const&)不起作用,因为要构造一个由 T 组成的数组的数组,最外面的一个期望一个由 T 组成的数组作为参数,它期望一个由 T 组成的数组作为参数,它需要一个 T - 那里有太多级别的用户定义构造。

使用上面的模板构造函数,数组 T 的数组接受任何类型作为构造函数。与 T 的数组的数组一样,T 的数组也是如此。最后,无论您构造了 T 的数组的最外层数组,都将 T 传递给它,如果它不喜欢它,则会出现编译器错误几乎完全不可读的消息。

于 2012-11-05T16:44:15.273 回答