Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > работа с трехмерными массивами


Автор: ElKa 12.9.2010, 18:24
подскажите пожалуйста как лучше организовать работу с трехмерными массивами
с С++ я только начала работать, еще многого не знаю

надо написать программу, обрабатывающую информацию о трехмерных объемах
т.е. надо будет в трехмерных, двумерных и одномерных массивах сохранять данные и постоянно с ними оперировать

вопросы такие
- стоит ли создавать классы для массивов и операторы в них для более удобной обработки, напр. оператор присваивания массива и т.д. и классы Array3, Array2, Array
- как декларировать указатель на трехмерный массив и инициализировать сам массив
- как удалять многомерные массивы? так же, как и одномерные? delete [] p..
- если трехмерные массивы могут иметь две размерности, а операции с ними одни и те же, как лучше с ними работать? 



Автор: vnf 12.9.2010, 19:38
Цитата

- стоит ли создавать классы для массивов и операторы в них для более удобной обработки, напр. оператор присваивания массива и т.д. и классы Array3, Array2, Array

Стоит, очень стоит

Цитата

- как декларировать указатель на трехмерный массив и инициализировать сам массив

Как указатель на объект класса массива 
CMySuperArray * superarray = new CMySuperArray(5,5,8);
Цитата

- как удалять многомерные массивы? так же, как и одномерные? delete [] p..

delete superarray

Цитата

- если трехмерные массивы могут иметь две размерности, а операции с ними одни и те же, как лучше с ними работать? 


можно трактавать двухмерный массив как частный случай трёхмерного у которого одна из размерностей 1
можно трактавать одномерный массив как частный случай трёхмерного у которого две из размерностей 1
т.е. достаточного одного класса трехмерного массива

реализовать такой класс можно попробовать с помощью vector


Автор: ElKa 19.9.2010, 23:30
vnf, 

спасибо! что-то я не разберусь с этими массивами

мне нужен класс для массивов, в котором можно будет размерность устанавливать
я написала темплейт и почему-то ошибки 
посмотрите пожалуйста

array.h
Код

#ifndef ARRAY_H_
#define ARRAY_H_
 
template <typename T, int s1, int s2, int s3>
class Array{

private:
    T *a_;
    int _size1;
    int _size2;
    int _size3;

public:
    Array();

    int getSize1();
    int getSize2();
    int getSize3();
    T &getValue(int i, int j, int k);
    void setValue(const T &value, int i, int j, int k);

    Array &operator=(Array &right);

    int &operator!=(Array &right);

    T &operator [](int i);

};


#endif /* ARRAY_H_ */

#include "array.cxx"



array.cxx
Код

#include <assert.h>

//!
/*!

  */
template <typename T, int s1, int s2, int s3>
Array<T,s1,s2,s3>::Array(): a_(new T[s1][s2][s3]), _size1(s1), _size2(s2), _size3(s3){}

template <typename T, int s1, int s2, int s3>
int Array<T,s1,s2,s3>::getSize1(){return _size1;}

template <typename T, int s1, int s2, int s3>
int Array<T,s1,s2,s3>::getSize2(){return _size2;}

template <typename T, int s1, int s2, int s3>
int Array<T,s1,s2,s3>::getSize3(){return _size3;}

template <typename T, int s1, int s2, int s3>
T &Array<T,s1,s2,s3>::getValue(int i, int j, int k){ return a_[i][j][k];}

template <typename T, int s1, int s2, int s3>
void Array<T,s1,s2,s3>::setValue(const T &value, int i, int j, int k){a_[i][j][k] = value;}

template <typename T, int s1, int s2, int s3>
Array &Array<T,s1,s2,s3>::operator=(Array &right){
        if(&right != this)
        {
            delete [] a_;
            _size1 = right.size1;
            _size2 = right.size2;
            _size3 = right.size3;
            a_ = new T[_size1][_size2][_size3];
            assert(a_ != 0);

            for(int i; i<_size1; ++i)
                for(int j; j<_size2; ++j)
                    for(int k; k<_size3; ++k)
                        a_[i][j][k] = right[i][j][k];
        }
        return *this;
    }

template <typename T, int s1, int s2, int s3>
int &Array<T,s1,s2,s3>::operator!=(Array &right)
    {
        if(_size1 != right.getSize1() || _size2 != right.getSize2() || _size3 != right.getSize3())
            return 1;

        for(int i; i<_size1; ++i)
            for(int j; j<_size2; ++j)
                for(int k; k<_size3; ++k)
                    if(a_[i][j][k] != right[i][j][k]) return 1;

        return 0;
    }

template <typename T, int s1, int s2, int s3>
T &Array<T,s1,s2,s3>::operator [](int i){return a_[i];}


вот здесь ошибка
Array &Array<T,s1,s2,s3>::operator=(Array &right)
говорит expected constructor, destructor, or type conversion before '&' token

и еще вопрос
как мне декларировать и создавать объекты этого класса в других классах?

Array [][][] arr;

int arr[2][3][5];

 

Автор: vnf 20.9.2010, 07:19
Код

Array &Array<T,s1,s2,s3>::operator=(Array &right)


должно быть 

Код

Array<T,s1,s2,s3> &Array<T,s1,s2,s3>::operator=(Array<T,s1,s2,s3> &right)


то что в угловых скобках является неотемлимой часть имени класса

соответственно при использовании класса

Код

Array<int,7,8,10> arr


Вопрос: у вас везде будут испоьлзоваться массивы одинаковой размерности? Если нет, то размерности лs1 s2 s3 лучше не включать в праметры шаблона, а сделать аргументами конструктора

Код

template <typename T>
class Array{

private:
    T *a_;
    int _size1;
    int _size2;
    int _size3;

public:
    Array(int s1, int s2, int s3);

    int getSize1();
    int getSize2();
    int getSize3();
    T &getValue(int i, int j, int k);
    void setValue(const T &value, int i, int j, int k);

    Array &operator=(Array &right);

    int &operator!=(Array &right);

    T &operator [](int i);

};

template <typename T>
Array<T>::Array(int s1, int s2, int s3): a_(new T[s1][s2][s3]), _size1(s1), _size2(s2), _size3(s3){}


использование

Код

Array<int> arr(7,8,10);


ещё вашему классу нехватает деструктора, в котором нужно будет освобождать память delete [] a_

Автор: ElKa 20.9.2010, 22:12
vnf, 

спасибо, сейчас все внедрю и посмотрю, что будет  

Автор: Master01 20.9.2010, 22:44
Если это не учебный проект, цель которого именно в том, чтобы "научится создавать собственные трёхмерные массивы", а вам просто нужен в вашей программе трёхмерный массив, то можете воспользоваться готовой реализацией - boost::multi_array

вот пример из хелпа

Код

typedef boost::multi_array<double, 3> array_type;
 array_type A(boost::extents[3][4][2]);
 A[0][0][0] = 3.14;
 assert(A[0][0][0] == 3.14);


Добавлено через 5 минут и 19 секунд
однако ничего так код для человека, который "с С++ я только начала работать, еще многого не знаю" smile

Автор: ElKa 21.9.2010, 21:48
Master01, 

спасибо, этот вариант тоже попробую.. а проект не учебный, а очень сурьезный  

опять ошибка у меня.. посмотрите пожалуйста
пока код выглядит так

array.h
Код

#ifndef ARRAY_H_
#define ARRAY_H_
 
template <typename T>
class Array{

public:
    Array(int, int, int);
    ~Array();
    int getSize1();
    int getSize2();
    int getSize3();
    T &getValue(int i, int j, int k);
    void setValue(const T &value, int i, int j, int k);

    Array &operator=(Array &right);

    int &operator!=(Array &right);

    T &operator [](int i);
private:
    T *a_;
    int _size1;
    int _size2;
    int _size3;
};


#endif /* ARRAY_H_ */

#include "array.cxx"



array.cxx
Код

#include <assert.h>

template <typename T>
Array<T>::Array(int s1, int s2, int s3): a_(new T[s1][s2][s3]), _size1(s1), _size2(s2), _size3(s3){}

template <typename T>
Array<T>::~Array(){ delete [] a_;}

template <typename T>
int Array<T>::getSize1(){return _size1;}

template <typename T>
int Array<T>::getSize2(){return _size2;}

template <typename T>
int Array<T>::getSize3(){return _size3;}

template <typename T>
T &Array<T>::getValue(int i, int j, int k){ return a_[i][j][k];}

template <typename T>
void Array<T>::setValue(const T &value, int i, int j, int k){a_[i][j][k] = value;}

template <typename T>
Array<T> &Array<T>::operator=(Array<T> &right){
        if(&right != this)
        {
            delete [] a_;
            _size1 = right.size1;
            _size2 = right.size2;
            _size3 = right.size3;
            a_ = new T[_size1][_size2][_size3];
            assert(a_ != 0);

            for(int i; i<_size1; ++i)
                for(int j; j<_size2; ++j)
                    for(int k; k<_size3; ++k)
                        a_[i][j][k] = right[i][j][k];
        }
        return *this;
    }

template <typename T>
int &Array<T>::operator!=(Array &right)
    {
        if(_size1 != right.getSize1() || _size2 != right.getSize2() || _size3 != right.getSize3())
            return 1;

        for(int i; i<_size1; ++i)
            for(int j; j<_size2; ++j)
                for(int k; k<_size3; ++k)
                    if(a_[i][j][k] != right[i][j][k]) return 1;

        return 0;
    }

template <typename T>
T &Array<T>::operator [](int i){return a_[i];}



ошибка здесь
Array<T>::Array(int s1, int s2, int s3): a_(new T[s1][s2][s3]), _size1(s1), _size2(s2), _size3(s3){}

говорит  
    - `s2' cannot appear in a constant-
     expression
    - `s3' cannot appear in a constant-
     expression

Автор: vnf 21.9.2010, 22:01
Код

new T[s1][s2][s3]


на 
Код

new T[s1*s2*s3]



здесь
Код

        for(int i; i<_size1; ++i)
            for(int j; j<_size2; ++j)
                for(int k; k<_size3; ++k)
                    if(a_[i][j][k] != right[i][j][k]) return 1;


нужно принициализировать i,j,k, иначе на дебажной будет работать а в релизе ошибки посыплются (int i = 0 и т.д.)

в setValue и getValue нужен контроль на выход за границы массива

Автор: RedDigger 25.9.2010, 02:52
Прошу прощения что влезаю в данный топик но не легче ли будет использовать обычные динамические массивы.. 
Создать класс для обработки 3х мерных массивов довольно просто. инициализировать подобные элементы также труда составить не должно.

Вот рабочая программка только что наваянная:
main.cpp
Код

#include "myarray.h"
#include <iostream>
using namespace std;

int main() {
        MyArray *array = new MyArray(3,3,3);
        bool ok;
        int result;
        // Writing test
        // Will be ok;
        if(array->setValue(2,2,1,225)) {
                cout << "Error." << endl;
        } else  cout << "Correct expected" << endl;

        // Will Fail
        if(array->setValue(2,6,1,225)) {
                cout << "Expected Error." << endl;
        } else  cout << "Correct" << endl;

        // Reading test
        // Will be ok
        result = array->getValue(2,2,1,ok);
        if(!ok) {
                cout << "Error" << endl;
        } else  cout << "Correct expected; Value: " << result << endl;
        // Will fail
        result = array->getValue(2,6,1,ok);
        if(!ok) {
                cout << "Error Expected" << endl;
        } else  cout << "Correct" << endl;


        delete array;
        return 0;
}

myarray.h
Код

#ifndef __MYARRAY_H
#define __MYARRAY_H

class MyArray {
public :
        // Constructor for array
        MyArray(int, int, int);
        ~MyArray();
        // accessors
        bool setValue(int, int, int, int);
        int getValue(int, int, int, bool&);
private:
        // Array pointer
        int *** m_array;
        // Maximums
        int m_xMax;
        int m_yMax;
        int m_zMax;
};

#endif


myarray.cpp 
Код

#include "myarray.h"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

using namespace std;

MyArray::MyArray(int xMax, int yMax, int zMax) {
        int x,y,z;
        m_array = (int ***)malloc( xMax * sizeof(int));
        for(x = 0; x < xMax; x++) {
                m_array[x] = (int **) malloc( yMax * sizeof(int) );
                for(y = 0; y < yMax; y++) {
                        m_array[x][y] = (int *) malloc( zMax * sizeof(int) );
                        for(z = 0; z < zMax; z++) {
                                m_array[x][y][z] = 0;
                        }
                }
        }
        m_xMax = xMax;
        m_yMax = yMax;
        m_zMax = zMax;
        cout << "Object created" << endl;
}
MyArray::~MyArray() {
        int x,y;
        for(x = 0; x < m_xMax; x++) {
                for(y = 0; y < m_yMax; y++) {
                        delete m_array[x][y];
                }
                delete m_array[x];
        }
        cout << "Object deleted" << endl;

}
int MyArray::getValue(int x, int y, int z, bool &ok) {
        if(x >= m_xMax) {
                memset(&ok,0,1);
                return 0;
        }
        if(y >= m_yMax) {
                memset(&ok,0,1);
                return 0;
        }
        if(z >= m_zMax) {
                memset(&ok,0,1);
                return 0;
        }
        memset(&ok,1,1);
        return m_array[x][y][z];
}

bool MyArray::setValue(int x, int y, int z, int value) {
        if(x >= m_xMax) 
                return true;

        if(y >= m_yMax) 
                return true;

        if(z >= m_zMax) 
                return true;
        m_array[x][y][z] = value;
        return false;
}



Под линуксом работает нормально.. винды нет для проверки но теоретически работать должна и там.. Возможно изза разных компиляторов может чтото ругаться при сборке но все должно правиться без особого труда..

Надеюсь информация поможет.

Автор: ValeryLaptev 25.9.2010, 07:41
Если проект серьезный, как утверждает Elka, то лучше не писать велосипед, а использовать вектор векторов векторов. Из стандартной библиотеки.
 

Автор: RedDigger 25.9.2010, 13:03
Цитата(ValeryLaptev @ 25.9.2010,  07:41)
Если проект серьезный, как утверждает Elka, то лучше не писать велосипед, а использовать вектор векторов векторов. Из стандартной библиотеки.

Но при этом не учитывается что вектор занимает в памяти намного больше места нежели обычный массив. А если данных будет очень много??? результатом будет разница в скорости работы....

Холивар поднимать не хочу.. мое дело предложить.

Автор: ElKa 25.9.2010, 19:38
RedDigger, 

спасибо, попробую и так


ValeryLaptev, 

мне нужны именно массивы, потому что слишком много данных 

Автор: vnf 26.9.2010, 09:07
ElKa

Вопрос: на сколько больше памяти занимает vector по сравнению с массивом?

Автор: RedDigger 26.9.2010, 21:38
Вектор - это структурный объект.. У него есть несколько больше возможностей и параметров которые возомжно не нужны в примере.. возможно следует только создать массив, удалить его при выходе и иметь возможность положить туда чтолибо или вытащить с него... 
Для огромного объема данных приходится иногда изобретать велосипед.. иногда и 28.4 колесный... лично мне не привыкать smile

Автор: JackYF 29.9.2010, 23:09
Цитата(RedDigger @  25.9.2010,  12:03 Найти цитируемый пост)
Но при этом не учитывается что вектор занимает в памяти намного больше места нежели обычный массив.

Откуда информация?

Автор: RedDigger 30.9.2010, 00:15
Цитата(JackYF @ 29.9.2010,  23:09)
Цитата(RedDigger @  25.9.2010,  12:03 Найти цитируемый пост)
Но при этом не учитывается что вектор занимает в памяти намного больше места нежели обычный массив.

Откуда информация?

Программа номер раз
Код

#include "boost/multi_array.hpp"
#include <iostream>

using namespace std;

int main(int argc, char ** argv) {
         typedef boost::multi_array<double, 3> array_type;
         array_type A(boost::extents[3][4][2]);
         printf("Sizeof boost::multi_array is %d\n", sizeof(A));
         return 0;
}



вывод - 
Код

Sizeof boost::multi_array is 80


программа номер два..
Код

#include "myarray.h"
#include <iostream>
using namespace std;

int main() {
        MyArray *array = new MyArray(3,3,3);
        cout << "Size of MyArray is " << sizeof(*array) << endl;
}


вывод - 
Код

Size of MyArray is 16



Автор: Master01 30.9.2010, 13:33

Вы не совсем правильно понимаете эти результаты. Контейнер  будет занимать всегда на N  байт больше и это не зависит от количества элементов. Например, если память мне не изменяет, std::vector даже пустой (не содержащий полезных данных) занимает  где-то 20 байт.

т.о. если говорить, к примеру, о массиве int из 1024 элементов, требующих для хранения (предполагаем что int = 4 байта) 4 Кб, то эти 20 байт уже не кажутся таким кащунственным разбазареванием ресурсов.

А лепить что-то своё, ради выйгрыша в 20 байт весьма сомнительно ...

а вот политика выделения памяти, которой следует тот или иной контейнер - это действительно то, на что слежует обратить внимание, если нужно что-то оптимизировать.

Автор: RedDigger 30.9.2010, 13:36
Не учел... Спасибо за разъеснение.   

Автор: Леопольд 30.9.2010, 16:29
Цитата(RedDigger @  25.9.2010,  13:03 Найти цитируемый пост)
Но при этом не учитывается что вектор занимает в памяти намного больше места нежели обычный массив.
Проблемма не в этом а в копировании векторов, которые являются элементами вектора. Делается глубокое копирование. Т.е. swap двух векторов один/два раза выделяется память под новый массив данных и трижды делается глубокая копия. IMHO это несколько расточительно.

Тут вроде-бы в помощь shared_ptr, но для того что-бы достучаться до элемента, придётся делать как-то так:
Код
typedef boost::shared_ptr<std::vector<int> > dim1_t;
typedef boost::shared_ptr<std::vector<dim1_t> > dim2_t;

std::vector<dim2_t> array;

array.push_back(dim2_t(new std::vector<dim1_t>));
array.back()->push_back(dim1_t(new std::vector<int>));
array.back()->back()->push_back(123);
std::cout<<(*(*array[0])[0])[0]<<std::endl;
// и т.д. и т.п.

Лично мне такой вариант не очень...

Автор: JackYF 30.9.2010, 20:50
Цитата(Леопольд @  30.9.2010,  15:29 Найти цитируемый пост)
трижды делается глубокая копия

Что-то я не понял, почему трижды. При глубоком копировании вектора векторов векторов векторов ... любой степени вложенности всё копируется единожды. По крайней мере, если вектор == std::vector. Но так должно быть и у любого другого вменяемого вектора.

Автор: Леопольд 2.10.2010, 10:03
Цитата(JackYF @  30.9.2010,  20:50 Найти цитируемый пост)
Что-то я не понял, почему трижды.
Я говорил про swap, который повсеместно используется в STL. Он создаёт временную копию.

Автор: mes 2.10.2010, 10:20
Цитата(Леопольд @  30.9.2010,  15:29 Найти цитируемый пост)
Т.е. swap двух векторов 


Цитата(Леопольд @  2.10.2010,  09:03 Найти цитируемый пост)
Я говорил про swap, который повсеместно используется в STL. Он создаёт временную копию.

я Вас правильно понял, что swap одного вектора в другой медленее чем просто копия ?



Автор: ElKa 2.10.2010, 17:25
RedDigger, 

а как передавать такие массивы объектам других классов? 

мне нужно создавать объекты, которые содержат по два трехмерных массива 
в общем конструктор должен быть такой

Код

WCl::WCl(MyArray  p, MyArray n, int id)
{
    _p = p;
    _n = n;
    _id = id;
    }


и операторы наверное же тоже нужны

myarray.h
Код

        MyArray &operator=(MyArray &right);
        int &operator!=(MyArray &right);
        int &operator [](int i);

myarray.cxx
Код

MyArray &MyArray::operator=(MyArray &right){
        if(&right != this)
        {
            delete [] a_;
            _size1 = right.getSize1();
            _size2 = right.getSize2();
            _size3 = right.getSize3();
            a_ = new T[_size1*_size2*_size3];
            assert(a_ != 0);

            for(int i=0; i<_size1; ++i)
                for(int j=0; j<_size2; ++j)
                    for(int k=0; k<_size3; ++k)
                        a_[i*j*k] = &right[i*j*k];
        }
        return *this;
    }

int &MyArray::operator!=(MyArray &right)
    {
        if(_size1 != right.getSize1() || _size2 != right.getSize2() || _size3 != right.getSize3())
            return 1;

        for(int i=0; i<_size1; ++i)
            for(int j=0; j<_size2; ++j)
                for(int k=0; k<_size3; ++k)
                    if(a_[i][j][k] != right[i][j][k]) return 1;

        return 0;
    }

int &MyArray::operator [](int i)
    {
       return a_[i];
    }




Автор: JackYF 2.10.2010, 21:08
Цитата(Леопольд @  2.10.2010,  09:03 Найти цитируемый пост)
Я говорил про swap, который повсеместно используется в STL. Он создаёт временную копию.

Я сомневаюсь, что swap двух векторов делает временную копию. Проверяли?

Автор: RedDigger 2.10.2010, 21:32
Цитата(ElKa @ 2.10.2010,  17:25)
RedDigger, 

а как передавать такие массивы объектам других классов? 

мне нужно создавать объекты, которые содержат по два трехмерных массива 
в общем конструктор должен быть такой

Код

WCl::WCl(MyArray  p, MyArray n, int id)
{
    _p = p;
    _n = n;
    _id = id;
    }


и операторы наверное же тоже нужны

myarray.h
Код

        MyArray &operator=(MyArray &right);
        int &operator!=(MyArray &right);
        int &operator [](int i);

myarray.cxx
Код

MyArray &MyArray::operator=(MyArray &right){
        if(&right != this)
        {
            delete [] a_;
            _size1 = right.getSize1();
            _size2 = right.getSize2();
            _size3 = right.getSize3();
            a_ = new T[_size1*_size2*_size3];
            assert(a_ != 0);

            for(int i=0; i<_size1; ++i)
                for(int j=0; j<_size2; ++j)
                    for(int k=0; k<_size3; ++k)
                        a_[i*j*k] = &right[i*j*k];
        }
        return *this;
    }

int &MyArray::operator!=(MyArray &right)
    {
        if(_size1 != right.getSize1() || _size2 != right.getSize2() || _size3 != right.getSize3())
            return 1;

        for(int i=0; i<_size1; ++i)
            for(int j=0; j<_size2; ++j)
                for(int k=0; k<_size3; ++k)
                    if(a_[i][j][k] != right[i][j][k]) return 1;

        return 0;
    }

int &MyArray::operator [](int i)
    {
       return a_[i];
    }


Лучше всего передовать их по ссылке. В данном случае стек вызовов не переполняется лишними объектами. А внутри класса куда тебе надо завести данные создаешь член класса типа MyArray * (допустим)_b. 

Но с операторами ты немного намудрила. впринципе могу помочь.. 
Мои координаты - 
Skype: sl.spell
Mail: pn.spell{at}gmail.com

Автор: Леопольд 3.10.2010, 17:01
Цитата(JackYF @  2.10.2010,  21:08 Найти цитируемый пост)
Я сомневаюсь, что swap двух векторов делает временную копию. Проверяли? 
Неа, не проверял, а зря. Вызывается векторный swap...

Автор: mes 3.10.2010, 17:33
Цитата(Леопольд @  3.10.2010,  16:01 Найти цитируемый пост)
 а зря

smile
Цитата

23.2.4.4 vector specialized algorithms
   template <class T, class Allocator>
   void swap(vector<T,Allocator>& x, vector<T,Allocator>& y);
 Effects:
   x.swap(y);

Автор: ElKa 5.10.2010, 22:54
посмотрите пожалуйста код
почему то не те элементы вызываются и зависает ехе
массивы 3мерные и что-то я с этими указателями запуталась
и еще меня смущает operator [](int i), я его оставила таким же как для одномерных массивов, это правильно? 

array.h
Код

#ifndef ARRAY_H_
#define ARRAY_H_

class Array {
public:
    Array();
        Array(int, int, int);
        ~Array();
        int getSize1();
        int getSize2();
        int getSize3();
        bool setValue(int, int, int, float); 
        float getValue(int, int, int);
        Array &operator=(Array &right);
        int operator!=(Array &right);
        float** &operator[](int);

private:
        float *** a_;
        int _size1;
        int _size2;
        int _size3;
};

#endif


array.cxx
Код

#include "array.h"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

using namespace std;

Array::Array(){}

Array::Array(int size1, int size2, int size3) {
       int i,j,k;
       a_ = (float ***)malloc( size1 * sizeof(int));
       // **a_ = new float[size1];
        for(i = 0; i < size1; i++) {
            a_[i] = (float **) malloc( size2 * sizeof(int) );
         //*a_[i] = new float[size2];
                for(j = 0; j < size2; j++) {
                    a_[i][j] = (float *) malloc( size3 * sizeof(int) );
             //    a_[i][j] = new float[size3];
                        for(k = 0; k < size3; k++) {
                                a_[i][j][k] = 0;
                        }
                }
        }
        _size1 = size1;
        _size2 = size2;
        _size3 = size3;
        cout << "array created" << endl;
}

Array::~Array() {
        int i,j;
        for(i = 0; i < _size1; i++) {
                for(j = 0; j < _size2; j++)
                        delete a_[i][j];
                delete a_[i];
        }
        cout << "array deleted" << endl;
}

int Array::getSize1(){
    return _size1;
}

 int Array::getSize2(){
     return _size2;
 }

 int Array::getSize3(){
     return _size3;
 }

bool Array::setValue(int x, int y, int z, float value) {
        if(x >= _size1)
                return true;
        if(y >= _size2)
                return true;
        if(z >= _size3)
                return true;
        a_[x][y][z] = value;
        return false;
}
   
float Array::getValue(int x, int y, int z) {
     if(x>=_size1 || y>=_size2 || z>=_size3){
         cout << "index out of range" << endl;
         return 0;
     }
     return a_[x][y][z];
}

Array &Array::operator=(Array &right){
       int i,j,k; 
       if(&right != this)
       {
           delete [] a_; 
           _size1 = right.getSize1(); 
           _size2 = right.getSize2(); 
           _size3 = right.getSize3(); 
           a_ = (float ***)malloc(_size1*_size2*_size3);
           assert(a_ != 0);

           for(i=0; i<_size1; i++){
              for(j=0; j<_size2; j++){
                 for(k=0; k<_size3; k++){
                  a_[i][j][k] = right[i][j][k]; //  вот здесь проблема!!!
                 }
              }
           }
                 }
        return *this;
}

int Array::operator!=(Array &right)
{
        if(_size1 != right.getSize1() || _size2 != right.getSize2() || _size3 != right.getSize3())
                return 1;

        int i,j,k;
        for(i=0; i<_size1; ++i)
                for(j=0; j<_size2; ++j)
                        for(k=0; k<_size3; ++k)
                                if(a_[i][j][k] != right[i][j][k]) return 1;

        return 0;
 }

float** &Array::operator [](int i)
    {
       return a_[i];
    }


 

Автор: Леопольд 6.10.2010, 09:59
ElKa, c двумерными массивами код уже  сложно читать, а с тремя измерениями становится совсем тяжко, что чревато багами.

Индексы лучше переименовать в column, row, dept (или x, y ,z) во всём коде.

Если производительность не критична, то можно попробовать уйти от трехмерных массивов. Например, использовать что-то типа std::map<Point3d, float>.

IMHO логика в Array::setValue извращённая, если выход за границы, возвращает true, если всё ОК, возвращает false.

Макроса assert в релизе обычно нет (#define NDEBUG). Проверять им что недостаточно памяти не стоит.

Использовать malloc a потом delete[], это, по моему уже UB(undefined behaviour). Вместо malloc надо использовать new[]; Вызывает подозрение что на три выделения памяти делается два delete (а не три).

Если есть возможность то лучше использовать исключения (или assert, если позволяет специфика приложения) в Array::getValue и Array::setValue. Исключения более универсальны, assert не обработаешь, но зато нет накладных расходов.

В Array::operator= утечка памяти. Вместо трёх delete, всего один. И вообще, возникает ощущение что код писали как в мульте "Простоквашино" писали письмо. Строчку ты, строчку твой кот, а всё остальное Шарик... smile
Код
       a_ = (float ***)malloc( size1 * sizeof(int));
       // **a_ = new float[size1];
        for(i = 0; i < size1; i++) {
            a_[i] = (float **) malloc( size2 * sizeof(int) );
         //*a_[i] = new float[size2];
                for(j = 0; j < size2; j++) {
                    a_[i][j] = (float *) malloc( size3 * sizeof(int) );
             //    a_[i][j] = new float[size3];
                        for(k = 0; k < size3; k++) {
                                a_[i][j][k] = 0;
                        }
                }
        }
Код
a_ = (float ***)malloc(_size1*_size2*_size3);


Цитата(ElKa @  5.10.2010,  22:54 Найти цитируемый пост)
посмотрите пожалуйста код
Возникает ощущение что ты сама его не смотрела. Видимо практикуешь "слепой" десятипальцевый набор с завязанными глазами.. smile

P.S.
А может это просто стёб? Бывает и такое. Весьма похоже, надо сказать.

Автор: Леопольд 6.10.2010, 12:05
Если оно надо, то можно допилить.

Код

#ifndef MULTI_ARRAY_HPP
#define MULTI_ARRAY_HPP

#include <cassert>
#include <cstdlib>
#include <cstring>

#include <stdexcept>
#include <algorithm>

template<typename T, std::size_t dimension>
class MultiArray
{
private:
    friend class MultiArray<T, dimension + 1>;

    T * m_data_;

    // lArg must not be zero
    static inline std::size_t multiply_(std::size_t lArg, std::size_t rArg)
    {
        std::size_t product = lArg * rArg;
        if(product / lArg != rArg)
        {
            throw std::overflow_error("MultiArray: std::size_t overflow");
        }
        return product;
    }

    static void calculateSizes_(std::size_t const (&init)[dimension], std::size_t (&sizes)[dimension])
    {
        sizes[dimension - 1] = init[dimension - 1];
        for(std::size_t i = 1; i < dimension; ++i)
        {
            std::size_t const next = dimension - i - 1;
            std::size_t const curr = dimension - i;

            sizes[next] = multiply_(sizes[curr], init[next]);
            if(sizes[next] == 0)
            {
                throw std::length_error("MultiArray: zero length");
            }
        }
    }

    static T * alloc_(std::size_t const (&init)[dimension])
    {
        std::size_t sizes_[dimension];
        calculateSizes_(init, sizes_);

        //sizes_[0] contains the array's total size
        std::size_t const sizeofPayload_ = multiply_(sizes_[0], sizeof(T));
        std::size_t const sizeofWithoutPayload_ = sizeof(std::size_t) * (dimension + 2);

        std::size_t * buffer_ = static_cast<std::size_t *>(::malloc(sizeofWithoutPayload_ + sizeofPayload_));
        if(buffer_ == NULL)
        {
            throw std::bad_alloc();
        }

        // init data buffer_
        buffer_[0] = dimension;
        std::copy(sizes_, sizes_ + dimension, buffer_ + 1);
        buffer_[dimension + 1] = 1;

        // construct data
        T * payload_ = reinterpret_cast<T *>(buffer_ + dimension + 2);
        for(std::size_t i = 0; i < sizes_[0]; ++i)
        {
            new(payload_ + i) T();
        }

        return payload_;
    }

    static T * alloc_(MultiArray const& copy)
    {
        std::size_t const sizeofWithoutPayload_ = sizeof(std::size_t) * (dimension + 2);
        std::size_t const sizeofPayload_ = copy.size() * sizeof(T);

        std::size_t * buffer_ = static_cast<std::size_t *>(::malloc(sizeofWithoutPayload_ + sizeofPayload_));
        if(buffer_ == NULL)
        {
            throw std::bad_alloc();
        }
        std::size_t * copyBuffer_ = reinterpret_cast<std::size_t *>(copy.m_data_) - dimension - 2;
        std::copy(copyBuffer_, copyBuffer_ + dimension + 2, buffer_);

        // construct data
        T * payload_ = reinterpret_cast<T *>(buffer_ + dimension + 2);
        std::copy(copy.m_data_, copy.m_data_ + copy.size(), payload_);

        return payload_;
    }

    void destroy_() throw()
    {
        std::size_t const size_ = this->size();
        for(std::size_t i = 0; i < size_; ++i)
        {
            this->m_data_[i].~T();
        }
        std::size_t * rawMemory_ = reinterpret_cast<std::size_t *>(this->m_data_) - dimension - 2;
        ::free(rawMemory_);
    }

    struct Proxy_
    {
        std::size_t * m_sizes;
        T * m_data;

        Proxy_(std::size_t * sizes, T * data) :
            m_sizes(sizes), m_data(data)
        {}

        typename MultiArray<T, dimension - 1>::Proxy_ operator[] (std::size_t i)
        {
            T * leftBound = this->m_data;
            T * rightBound = this->m_data + this->m_sizes[0] - 1;

            std::size_t * sizes_ = this->m_sizes + 1;
            T * data_ = leftBound + multiply_(sizes_[0], i);
            if(data_ < leftBound || rightBound < data_)
            {
                throw std::range_error("MultiArray: out of bounds");
            }
            return MultiArray<T, dimension - 1>::getProxy_(sizes_, data_);
        }

        std::size_t size() const
        {
            return *this->m_sizes[0];
        }

        Proxy_ & operator=(Proxy_ const& rArg)
        {
            std::size_t const sizeofThis = this->m_sizes[0];
            std::size_t const sizeofThat = rArg.m_sizes[0];
            if(sizeofThis != sizeofThat)
            {
                throw std::length_error("MultiArray: unequal lenght");
            }
            std::copy(rArg.m_data, rArg.m_data + sizeofThat, this->m_data);

            return *this;
        }
    };

    struct NothrowProxy_
    {
        std::size_t * m_sizes;
        T * m_data;

        MultiArray *const m_parent;

        NothrowProxy_(std::size_t * sizes, T * data, MultiArray *const parent = NULL) :
            m_sizes(sizes), m_data(data), m_parent(parent)
        {}

        typename MultiArray<T, dimension - 1>::NothrowProxy_ operator[] (std::size_t i)
        {
            std::size_t * sizes_ = this->m_sizes + 1;
            T * data_ = this->m_data + sizes_[0] * i;
            return MultiArray<T, dimension - 1>::getNothrowProxy_(sizes_, data_);
        }

        std::size_t size() const
        {
            return this->m_sizes[0];
        }

        NothrowProxy_ & operator=(NothrowProxy_ const& rArg)
        {
            std::size_t const sizeofThat = rArg.m_sizes[0];
            std::copy(rArg.m_data, rArg.m_data + sizeofThat, this->m_data);

            return *this;
        }

//        TODO: array copy
        NothrowProxy_ & operator=(MultiArray const& copy)
        {
            if(this->m_parent != NULL)
            {
                T * buffer_ = alloc_(copy);
                this->m_parent->destroy_();
                this->m_parent->m_data_ = buffer_;
            }

            return *this;
        }

        void reshape(std::size_t const (&init)[dimension])
        {
            std::size_t sizes_[dimension];
            calculateSizes_(init, sizes_);

            //sizes_[0] contains the array's total size
            std::size_t * buffer_ = reinterpret_cast<std::size_t *>(this->m_data) - 1 - dimension;
            std::copy(sizes_ + 1, sizes_ + dimension, buffer_ + 1);
        }
    };

    static Proxy_ getProxy_(std::size_t * sizes, T * data)
    {
        return Proxy_(sizes, data);
    }

    static NothrowProxy_ getNothrowProxy_(std::size_t * sizes, T * data)
    {
        return NothrowProxy_(sizes, data);
    }

public:

    MultiArray(std::size_t const (&init)[dimension]) :
        m_data_(alloc_(init))
    {}

    MultiArray(MultiArray const& copy) :
        m_data_(alloc_(copy))
    {}

    typename MultiArray<T, dimension - 1>::Proxy_ operator[] (std::size_t i)
    {
        std::size_t * sizes_ = reinterpret_cast<std::size_t *>(this->m_data_) - dimension - 1;
        T * leftBound = this->m_data_;
        T * rightBound = this->m_data_ + sizes_[0] - 1;

        ++sizes_;
        T * data_ = leftBound + multiply_(sizes_[0], i);
        if(data_ < leftBound || rightBound < data_)
        {
            throw std::range_error("MultiArray: out of bounds");
        }
        return MultiArray<T, dimension - 1>::getProxy_(sizes_, data_);
    }

    NothrowProxy_ operator() (std::nothrow_t const&)
    {
        std::size_t * sizes_ = reinterpret_cast<std::size_t *>(this->m_data_) - dimension - 1;
        return NothrowProxy_(sizes_, this->m_data_, this);
    }

    MultiArray & operator= (MultiArray const& copy)
    {
        T * buffer_ = alloc_(copy);
        this->destroy_();
        this->m_data_ = buffer_;

        return *this;
    }

    void reshape(std::size_t const (&init)[dimension])
    {
        std::size_t sizes_[dimension];
        calculateSizes_(init, sizes_);

        //sizes_[0] contains the array's total size
        std::size_t * buffer_ = reinterpret_cast<std::size_t *>(this->m_data_) - 1 - dimension;
        if(sizes_[0] != buffer_[0])
        {
            throw std::invalid_argument("MultiArray: reshape unequal length");
        }

        std::copy(sizes_ + 1, sizes_ + dimension, buffer_ + 1);
    }

    ~MultiArray()
    {
        this->destroy_();
    }

    std::size_t size() const
    {
        return *(reinterpret_cast<std::size_t *>(this->m_data_) - dimension - 1);
    }
};

template<typename T>
class MultiArray<T, 0>
{
private:
    MultiArray(MultiArray const&);
    MultiArray & operator=(MultiArray const&);

    friend class MultiArray<T, 1>;

    typedef T & Proxy_;
    typedef T & NothrowProxy_;

    static Proxy_ getProxy_(std::size_t *, T * data)
    {
        return *data;
    }

    static NothrowProxy_ getNothrowProxy_(std::size_t *, T * data)
    {
        return *data;
    }

};

#endif
Код
#include <iostream>
#include <string>

#include <boost/multi_array.hpp>
#include <boost/lexical_cast.hpp>

#include "multi_array.hpp"

inline void testSpeed ()
{
    static std::size_t nativeArray[100][100][100];
    clock_t start = clock();
    for(std::size_t ni = 0; ni < 100; ++ni )
        for(std::size_t i = 0; i < 100; ++i)
            for(std::size_t j = 0; j < 100; ++j)
                for(std::size_t k = 0; k <100; ++k)
                    nativeArray[i][j][k] = 0;
    clock_t stop = clock();
    std::cout << "nativeArray: " << double((stop - start)) / CLOCKS_PER_SEC << " seconds" << std::endl;

    start = clock();
    typedef boost::multi_array<std::size_t, 3> array_type;
    typedef array_type::index index;
    array_type boostArray(boost::extents[100][100][100]);

    for(std::size_t ni = 0; ni < 100; ++ni )
        for(std::size_t i = 0; i < 100; ++i)
            for(std::size_t j = 0; j < 100; ++j)
                for(std::size_t k = 0; k <100; ++k)
                    boostArray[i][j][k] = 0;
    stop = clock();
    std::cout << "boostArray: " << double((stop - start)) / CLOCKS_PER_SEC << " seconds" << std::endl;

    start = clock();
    std::size_t init[] = {100, 100, 100};
    MultiArray<std::size_t, 3> myArray(init);

    for(std::size_t ni = 0; ni < 100; ++ni )
        for(std::size_t i = 0; i < 100; ++i)
            for(std::size_t j = 0; j < 100; ++j)
                for(std::size_t k = 0; k <100; ++k)
                    myArray(std::nothrow)[i][j][k] = 0;
    stop = clock();
    std::cout << "myArray(std::nothrow): " << double((stop - start)) / CLOCKS_PER_SEC << " seconds" << std::endl;

    start = clock();
    for(std::size_t ni = 0; ni < 100; ++ni )
        for(std::size_t i = 0; i < 100; ++i)
            for(std::size_t j = 0; j < 100; ++j)
                for(std::size_t k = 0; k <100; ++k)
                    myArray[i][j][k] = 0;
    stop = clock();
    std::cout << "myArray: " << double((stop - start)) / CLOCKS_PER_SEC << " seconds" << std::endl;
}

std::size_t testNo = 0;
bool printPassed = true;
#define BOOL_TEST(EXPR) \
++testNo;\
if(EXPR)\
{\
    if(printPassed)\
    {\
        std::cerr << "test " << testNo << ": PASSED (" #EXPR ")\n" << std::flush;  \
    }\
}\
else\
{\
    std::cerr << "test " << testNo << ": FAILED (" #EXPR ")\n" << std::flush;  \
}

inline void testSuite()
{
    {
        std::size_t init[] = {2, 5, 3};
        MultiArray<std::size_t, 3> array(init);

        std::size_t d0 = 0;
        std::size_t d1 = 0;
        std::size_t d2 = 0;

        BOOL_TEST(array.size() == 30)

        for(d0 = 0; d0 < init[0]; ++d0)
            for(d1 = 0; d1 < init[1]; ++d1)
                for(d2 = 0; d2 < init[2]; ++d2)
                {
                    array[d0][d1][d2] = d0 * 100 + d1 * 10 + d2;
                }

        for(d0 = 0; d0 < init[0]; ++d0)
            for(d1 = 0; d1 < init[1]; ++d1)
                for(d2 = 0; d2 < init[2]; ++d2)
                {
                    BOOL_TEST(array[d0][d1][d2] == d0 * 100 + d1 * 10 + d2);
                }

        array[0] = array[1];
        for(d1 = 0; d1 < init[1]; ++d1)
            for(d2 = 0; d2 < init[2]; ++d2)
            {
                BOOL_TEST(array[0][d1][d2] == array[1][d1][d2]);
            }

        array[0][0] = array[0][1];
        for(d2 = 0; d2 < init[2]; ++d2)
        {
            BOOL_TEST(array[0][0][d2] == array[0][1][d2]);
        }

        array[0][0][0] = array[0][0][1];
        BOOL_TEST(array[0][0][0] == array[0][0][1]);
    }

    {
        std::size_t init[] = {2, 3};
        MultiArray<std::size_t, 2> array(init);

        std::size_t d0 = 0;
        std::size_t d1 = 0;

        for(d0 = 0; d0 < init[0]; ++d0)
            for(d1 = 0; d1 < init[1]; ++d1)
            {
                array[d0][d1] = d0 * 10 + d1;
            }

        for(d0 = 0; d0 < init[0]; ++d0)
            for(d1 = 0; d1 < init[1]; ++d1)
            {
                BOOL_TEST(array[d0][d1] == d0 * 10 + d1);
            }

        std::size_t newShape1[] = {3, 2};
        array.reshape(newShape1);
        BOOL_TEST(array[0][0] == 00);
        BOOL_TEST(array[0][1] == 01);
        BOOL_TEST(array[1][0] == 02);
        BOOL_TEST(array[1][1] == 10);
        BOOL_TEST(array[2][0] == 11);
        BOOL_TEST(array[2][1] == 12);

        std::size_t newShape2[] = {1, 6};
        array.reshape(newShape2);
        BOOL_TEST(array[0][0] == 00);
        BOOL_TEST(array[0][1] == 01);
        BOOL_TEST(array[0][2] == 02);
        BOOL_TEST(array[0][3] == 10);
        BOOL_TEST(array[0][4] == 11);
        BOOL_TEST(array[0][5] == 12);

        std::size_t newShape3[] = {6, 1};
        array.reshape(newShape3);
        BOOL_TEST(array[0][0] == 00);
        BOOL_TEST(array[1][0] == 01);
        BOOL_TEST(array[2][0] == 02);
        BOOL_TEST(array[3][0] == 10);
        BOOL_TEST(array[4][0] == 11);
        BOOL_TEST(array[5][0] == 12);
    }

    {
        std::size_t init[] = {2, 5, 3};
        MultiArray<std::size_t, 3> array(init);

        std::size_t d0 = 0;
        std::size_t d1 = 0;
        std::size_t d2 = 0;

        BOOL_TEST(array(std::nothrow).size() == 30)

        for(d0 = 0; d0 < init[0]; ++d0)
            for(d1 = 0; d1 < init[1]; ++d1)
                for(d2 = 0; d2 < init[2]; ++d2)
                {
                    array(std::nothrow)[d0][d1][d2] = d0 * 100 + d1 * 10 + d2;
                }

        for(d0 = 0; d0 < init[0]; ++d0)
            for(d1 = 0; d1 < init[1]; ++d1)
                for(d2 = 0; d2 < init[2]; ++d2)
                {
                    BOOL_TEST(array(std::nothrow)[d0][d1][d2] == d0 * 100 + d1 * 10 + d2);
                }

        array(std::nothrow)[0] = array(std::nothrow)[1];
        for(d1 = 0; d1 < init[1]; ++d1)
            for(d2 = 0; d2 < init[2]; ++d2)
            {
                BOOL_TEST(array(std::nothrow)[0][d1][d2] == array(std::nothrow)[1][d1][d2]);
            }

        array(std::nothrow)[0][0] = array(std::nothrow)[0][1];
        for(d2 = 0; d2 < init[2]; ++d2)
        {
            BOOL_TEST(array(std::nothrow)[0][0][d2] == array(std::nothrow)[0][1][d2]);
        }

        array(std::nothrow)[0][0][0] = array(std::nothrow)[0][0][1];
        BOOL_TEST(array(std::nothrow)[0][0][0] == array(std::nothrow)[0][0][1]);
    }

    {
        std::size_t init[] = {2, 3};
        MultiArray<std::size_t, 2> array(init);

        std::size_t d0 = 0;
        std::size_t d1 = 0;

        for(d0 = 0; d0 < init[0]; ++d0)
            for(d1 = 0; d1 < init[1]; ++d1)
            {
                array(std::nothrow)[d0][d1] = d0 * 10 + d1;
            }

        for(d0 = 0; d0 < init[0]; ++d0)
            for(d1 = 0; d1 < init[1]; ++d1)
            {
                BOOL_TEST(array(std::nothrow)[d0][d1] == d0 * 10 + d1);
            }

        std::size_t newShape1[] = {3, 2};
        array(std::nothrow).reshape(newShape1);
        BOOL_TEST(array(std::nothrow)[0][0] == 00);
        BOOL_TEST(array(std::nothrow)[0][1] == 01);
        BOOL_TEST(array(std::nothrow)[1][0] == 02);
        BOOL_TEST(array(std::nothrow)[1][1] == 10);
        BOOL_TEST(array(std::nothrow)[2][0] == 11);
        BOOL_TEST(array(std::nothrow)[2][1] == 12);

        std::size_t newShape2[] = {1, 6};
        array(std::nothrow).reshape(newShape2);
        BOOL_TEST(array(std::nothrow)[0][0] == 00);
        BOOL_TEST(array(std::nothrow)[0][1] == 01);
        BOOL_TEST(array(std::nothrow)[0][2] == 02);
        BOOL_TEST(array(std::nothrow)[0][3] == 10);
        BOOL_TEST(array(std::nothrow)[0][4] == 11);
        BOOL_TEST(array(std::nothrow)[0][5] == 12);

        std::size_t newShape3[] = {6, 1};
        array(std::nothrow).reshape(newShape3);
        BOOL_TEST(array(std::nothrow)[0][0] == 00);
        BOOL_TEST(array(std::nothrow)[1][0] == 01);
        BOOL_TEST(array(std::nothrow)[2][0] == 02);
        BOOL_TEST(array(std::nothrow)[3][0] == 10);
        BOOL_TEST(array(std::nothrow)[4][0] == 11);
        BOOL_TEST(array(std::nothrow)[5][0] == 12);

        MultiArray<std::size_t, 2> array1(init);
        array1(std::nothrow) = array;
        BOOL_TEST(array1[0][0] == array(std::nothrow)[0][0]);
        BOOL_TEST(array1[1][0] == array(std::nothrow)[1][0]);
        BOOL_TEST(array1[2][0] == array(std::nothrow)[2][0]);
        BOOL_TEST(array1[3][0] == array(std::nothrow)[3][0]);
        BOOL_TEST(array1[4][0] == array(std::nothrow)[4][0]);
        BOOL_TEST(array1[5][0] == array(std::nothrow)[5][0]);

        array(std::nothrow).reshape(newShape2);
        array1 = array;
        BOOL_TEST(array(std::nothrow)[0][0] == array1[0][0]);
        BOOL_TEST(array(std::nothrow)[0][1] == array1[0][1]);
        BOOL_TEST(array(std::nothrow)[0][2] == array1[0][2]);
        BOOL_TEST(array(std::nothrow)[0][3] == array1[0][3]);
        BOOL_TEST(array(std::nothrow)[0][4] == array1[0][4]);
        BOOL_TEST(array(std::nothrow)[0][5] == array1[0][5]);
    }
}

int main()
{
    testSuite();

    testSpeed();

    return 0;
}

http://liveworkspace.org/code/48955ec79fab6268757bc9b65f3aad5f

Автор: Леопольд 7.10.2010, 13:39
Цитата(Леопольд @  6.10.2010,  12:05 Найти цитируемый пост)
Если оно надо, то можно допилить.
"допилил"...

Автор: ElKa 10.10.2010, 14:36
Цитата(Леопольд @ 6.10.2010,  09:59)
И вообще, возникает ощущение что код писали как в мульте "Простоквашино" писали письмо. Строчку ты, строчку твой кот, а всё остальное Шарик...  

ну в общем так и есть, если просмотреть комменты выше, то это должно быть понятно  smile 

за помощь спасибо, сейчас все внедрю и посмотрю, что будет 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)