我正在尝试更多地了解模板,并遇到了一个我似乎无法解决的问题。目前,下面的课程工作正常。
#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阵列,我可以使用普通数组括号访问它,例如一个[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类型时(即在尺寸&gt; 1上的任何数组上),这应该会失败,但事实并非如此。当数组是正方形时它可以工作,但如果没有,那么一些条目不会设置为10.有没有人知道标准向量类如何实现这一点?任何帮助都会很棒。谢谢!
答案 0 :(得分: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);
}
如果您使用的是可变参数模板,那么您可能会做一些更奇怪的事情,包括可变填充的初始化列表,但我认为我们已经做了足够的学习一周。
请注意使用new
时的基本缺陷:任何一个版本都要求您的类T
可以在某些“默认”状态下实例化,和它可以分配,甚至虽然我们从不要求第二个版本中的默认状态。这就是“真正的”库分离内存分配和对象构造的原因,除非它的位置版本,否则你永远不会看到new
表达式。
答案 1 :(得分:1)
std :: vector在内存块上使用placement 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,你必须首先分配内存,然后就地构造,然后记住在最后销毁每个元素。
与放置新路线相比,上述内容是半成品,因为我们首先构造每个元素,然后构建另一个元素,并使用operator=
覆盖它。
通过使其成为任意类型的模板构造函数,我们不依赖于多次转换来将多个级别放入数组中。天真的版本(你采用T const&amp;)不起作用,因为要构造一个T数组的数组数组,最外层的数组需要一个T数组的数组作为参数,它需要一个T数组作为参数,它期望一个T - 那里有太多的用户定义构造级别。
使用上面的模板构造函数,T数组的数组数组接受任何类型作为构造函数。就像T的数组数组一样,T的数组也是如此。最后,无论你构造T的数组最外层数组,都会传入T,如果它不喜欢它,你会得到一个编译器错误消息几乎完全不可读。
答案 2 :(得分: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_type
和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