Killing MD: 1D Mimics
数组降维:一维模拟
An MD array can be simulated in a 1D array as long as you know the dims. For example, if you want a 2x3 array, it would conceptually look like the following:
如果你知道一个MD(多维 == multi-dimensional)数组的维度,它就可以用一维数组来模拟。例如,你想要一个2×3的数组,概念上它看起来像这样:
0 1 2 3 4 5
This array has 6 elements (2 * 3). And you'll notice that each row starts with a multiple of the width (2). Therefore you can simulate a 2D array in a 1D array like so:
这个数组有6个元素(2*3)。而且你会发现,每行的宽度都为2。因此,你可以用一个一维数组来模拟这个二维数组,如下所示:
// int evil[5][6]; // desired 2D array int mimic[5*6]; // mimicing 1D array // evil[2][3] = 1; // desired [2][3] index mimic[ (2*6) + 3] = 1; // mimiced [2][3] indexAll that math looks kind of dumb and inefficient. Really, though, the compiler does all that math for MD arrays too, so this isn't any less efficient. It is pretty ugly though, granted.
int func2(int p[], int width); // remember this function? // it'll work with all flavors of mimicing 1D arrays! int a[ 5 * 6 ]; func2( a, 6 ); // works //--- int* b = new int[5*6]; func2( b, 6 ); // works //--- vector<int> c(5*6); func2( &c[0], 6 ); // works (but is a little ugly, granted)
数组降维:抽象数组类
So while 1D arrays are easier to work with, They do suffer from a few problems:
虽然一位数组很容易使用,但是也受一些问题困扰:
- look up requires you to know the dims of the array (can't do y*width
without knowing the width).
- look up syntax is ugly and easy to botch. It only gets worse as you get into larger dimensions. Imagine a mimiced 3D array:z*width*height + y*width + x
.
- 查找时,需要你知道数组的维数(如果不知道数组的宽度就不能使用 y*width
)。
- 查找语句看起来很很难看,而且很容易弄糟。只有在你面对更多的维数时,变得更加糟糕。想象一下模拟的3D数组:z*width*height + y*width + x
。
C++ provides an excellent solution to this problem: classes. Just make an Array2D/Matrix/whatever class to wrap all of this behavior.
C ++为这个问题提供了很好的解决方案:类。只要让 2D数组、矩阵、诸如此类 变成一个 类,然后包装所有的行为。
(Or don't -- there are probably many premade ones available online that you can use, I never actually bothered looking. Boost probably has one, I'd imagine -- I'll have to research this more some day. If you are reading further in this article I'm going to assume you don't have / don't want to use such a library and want to write your own)
While you can't overload the [bracket] operator to take two parameters*, you can overload the parenthesis operator. So syntax is a little different:
虽然你不能重载[ ]操作符来包含两个参数,你可以重载 括号操作符。因此,语法有点不一样:
// int bad[4][5]; Array2D<int> good(4,5); // bad[1][2] = 3; good(1,2) = 3;(* Yes you can overload the brakets and return a weird type to make double brakets work, but it causes all sorts of problems and defeats all the elegance of using a fully contained class)
这是一个非常简单的类,非常的朴实:
template <typename T> class Array2D { public: // constructor Array2D(unsigned wd,unsigned ht) : nWd(wd), nHt(ht), pAr(0) { if(wd > 0 && ht > 0) pAr = new T[wd * ht]; } // destructor ~Array2D() { delete[] pAr; } // indexing (parenthesis operator) // two of them (for const correctness) const T& operator () (unsigned x,unsigned y) const { return pAr[ y*nWd + x ]; } T& operator () (unsigned x,unsigned y) { return pAr[ y*nWd + x ]; } // get dims unsigned GetWd() const { return nWd; } unsigned GetHt() const { return nHt; } // private data members private: unsigned nWd; unsigned nHt; T* pAr; // to prevent unwanted copying: Array2D(const Array2D<T>&); Array2D& operator = (const Array2D<T>&);
如果你想让它变得更加理想,你可以添加上调整大小的函数,写时复制、引用计数,边界检查,等等,等等。
这部分内容仅做参考,有多种实现必然是好事,但是为何 straight array, nested new, vectors of vectors依然存在?存在即合 理,simple is beautiful...
写时复制(copy-on-write COW)http://www.cnblogs.com/MuyouSome/archive/2012/12/05/2802787.html
维基百科: 写入时复制(Copy-on-write)是一个被使用在程式设计领域的最佳化策略。其基础的观念是,如果有多个呼叫者(callers)同时要求相同资源,他们会共同取得相同的指标指向 相同的资源,直到某个呼叫者(caller)尝试修改资源时,系统才会真正复制一个副本(private copy)给该呼叫者,以避免被修改的资源被直接察觉到,这过程对其他的呼叫只都是 通透的(transparently)。此作法主要的优点是如果呼叫者并没有修改该资源,就不会有副本(private copy)被建立。 第一代Unix系统实现了一种傻瓜式的进程创建:当发出fork()系统调用时,内核原样复制父进程的整个地址空间并把复制的那一份分配给子进程。这种行为是非常耗时的,因为它 需要: - 为子进程的页表分配页帧 - 为子进程的页分配页帧 - 初始化子进程的页表 - 把父进程的页复制到子进程相应的页中 这种创建地址空间的方法涉及许多内存访问,消耗许多CPU周期,并且完全破坏了高速缓存中的内容。在大多数情况下,这样做常常是毫无意义的,因为许多子进程通过装入一个 新的程序开始它们的执行,这样就完全丢弃了所继承的地址空间。 现在的Linux内核采用一种更为有效的方法,称之为写时复制(Copy On Write,COW)。这种思想相当简单:父进程和子进程共享页帧而不是复制页帧。然而,只要页帧被共享, 它们就不能被修改,即页帧被保护。无论父进程还是子进程何时试图写一个共享的页帧,就产生一个异常,这时内核就把这个页复制到一个新的页帧中并标记为可写。原来的页帧 仍然是写保护的:当其他进程试图写入时,内核检查写进程是否是这个页帧的唯一属主,如果是,就把这个页帧标记为对这个进程是可写的。