查看完整版本: (已完成)指標觀念問題...應該吧
頁: [1]

baepi 發表於 2016-11-24 01:06 PM

(已完成)指標觀念問題...應該吧

本帖最後由 baepi 於 2016-11-29 03:46 PM 編輯

又是小弟我~繼上次問過創建二維動態記憶體~雖然大體已經成功~但是卻發現若是使用class物件~那麼並不會有建構的動作...聽不懂沒關係~畢竟這次跟上次那篇並無太大關係

這次小弟碰到的問題是...先看小弟完成的套件拙作
template<class T> T** new_new( int xx , int yy )
{
        T **temp = (T**)new T[ ( xx * sizeof(T*) ) + ( xx * yy )  ];        //[ 指標數量 + 大小 ]
        T *buf = (T *)( temp + xx );        //強制轉為1維陣列 + 略過指標數量
        temp[ 0 ] = buf;
        for( int i = 1 ; i < xx ; i++ )
        {
                temp[ i ] = temp[ i - 1 ] + yy ;
        }
        return temp;
}由此套件創建的二維動態記憶體....class元件會建構了~可是建構的值卻是錯誤的...這是問題1
建構的數量很明顯不對...這是問題2
下面有完整程式碼 + 執行結果~請高手們能給我解答或是提供嘗試的方向
#include <iostream>
using namespace std;
#define x 3
#define y 2
template<class T> T** new_new( int xx , int yy )
{
        T **temp = (T**)new T[ ( xx * sizeof(T*) ) + ( xx * yy )  ];        //[ 指標數量 + 大小 ]
        T *buf = (T *)( temp + xx );        //強制轉為1維陣列 + 略過指標數量
        temp[ 0 ] = buf;
        for( int i = 1 ; i < x ; i++ )
        {
                temp[ i ] = temp[ i - 1 ] + yy ;
        }
        return temp;
}
class test
{
public:
        int a;
        int b;
        test();
        ~test();
};
test::test()
{
        cout<<"這是建構\n";
        a = 7 ;
        b = 8;
}
test::~test()
{
        cout<<"這是解構\n";
}
void main()
{
cout<<"固定二維陣列\n";
        test t[ x ][ y ];
        for( int i = 0 ; i < x ; i++ )
        {
                for( int j = 0 ; j < y ; j++ )
                {
                        cout << t[ i ][ j ].a << t[ i ][ j ].b << '\t' ;
                }
                cout<<endl;
        }
        cout<<endl;

        cout<<"動態二維陣列\n";
        test **tt;
        tt = new test*[ x ];
        for( int i = 0 ; i < x ; i++ )
        {
                tt = new test[ y ];
        }
        for( int i = 0 ; i < x ; i++ )
        {
                for( int j = 0 ; j < y ; j++ )
                {
                        cout << tt[ i ][ j ].a << tt[ i ][ j ].b << '\t' ;
                }
                cout<<endl;
        }
        cout<<endl;
        for( int i = 0 ; i < x ; i++ )
        {
                delete [] tt;
        }
        delete [] tt;

        cout<<"一次刪除的動態二維陣列\n";
        test **ttt = new_new< test >( x , y );
        for( int i = 0 ; i < x ; i++ )
        {
                for( int j = 0 ; j < y ; j++ )
                {
                        cout << ttt[ i ][ j ].a << ttt[ i ][ j ].b << '\t' ;
                }
                cout<<endl;
        }
        cout<<endl;
        delete [] ttt;
}執行結果
固定二維陣列
這是建構
這是建構
這是建構
這是建構
這是建構
這是建構
78      78
78      78
78      78

動態二維陣列
這是建構
這是建構
這是建構
這是建構
這是建構
這是建構
78      78
78      78
78      78

這是解構
這是解構
這是解構
這是解構
這是解構
這是解構
一次刪除的動態二維陣列
這是建構
這是建構
這是建構
這是建構
這是建構
這是建構
這是建構
這是建構
這是建構
這是建構
這是建構
這是建構
這是建構
這是建構
這是建構
這是建構
這是建構
這是建構
87      87
87      87
87      87

這是解構
這是解構
這是解構
這是解構
這是解構
這是解構
請按任意鍵繼續 . . .已知錯誤T **temp = (T**)new T[ ( xx * sizeof(T*) ) + ( xx * yy )  ];        //[ 指標數量 + 大小 ]但是實在不知怎麼修改....

...<div class='locked'><em>瀏覽完整內容,請先 <a href='member.php?mod=register'>註冊</a> 或 <a href='javascript:;' onclick="lsSubmit()">登入會員</a></em></div><div></div>

johnwanz 發表於 2016-11-25 09:05 AM

new_new 第一行, 為何要
new T[ ( xx * sizeof(T*) ) + ( xx * yy )  ];

real data 只有xx*yy不是嗎?

cockroachrun 發表於 2016-11-25 09:36 AM

建構的數量當然不對啊
因為你寫的是
new T[ ( xx * sizeof(T*) ) + ( xx * yy )  ];
這是產生 class T 共 xx*sizeof(T*) + (xx*yy) 個
假設是在32bit 的pc windows OS 下 sizeof(T*) 為4
所以你一共產生了 xx*4 + xx*yy 個T 物件. 被建構的次數也是 xx*4 + xx*yy 次

怎麼改.. 分兩次new
一次 temp= new T*;
temp = new T;   之後的程式碼就一樣..

inunu 發表於 2016-11-28 03:27 AM

1. 把你 動態二維陣列 裡的做法整個代進去就對了

2. delete[] 本來就需要兩個層次, 否則就不是二維

cockroachrun 發表於 2016-11-28 09:11 AM

c++ 下沒辦法只有一次new . 因為 class 要construct . 也要 destruct
但又因為你要求的是兩個不同的class ( T*  跟 T 是不一樣的東西) 所以.. 沒有辦法做到.<br><br><br><br><br><div></div>

baepi 發表於 2016-11-28 10:17 AM

inunu 發表於 2016-11-28 03:27 AM static/image/common/back.gif
1. 把你 動態二維陣列 裡的做法整個代進去就對了

2. delete[] 本來就需要兩個層次, 否則就不是二維 ...

感謝還是有人給予回覆...其實我測試到現在已經半放棄了
delete要幾次跟幾維本質上是不太有直接關係的~是看你new幾次而論
雖然我上面的New示範錯超~~~大...但是註解已經講明為何只要一次delete的原理
在此也順便回答跟2樓有一樣問題的朋友好了
假設一個 int tt的陣列~他儲存的空間是2 x 3 = 6...但事實上還缺兩個tt 跟 tt這兩個指標的空間...因此今天假如能一次宣告完成~那delete勢必就能只做一次
那我發文時錯最大的地方在哪裡呢?恰恰是用new T要包含指標空間 + 儲存空間
因為指標空間在32bit時是4Byte...假如代入的T是4B~那還能用new T[ xx + ( xx * yy )  ];來讓錯的不小心可以正常運作~那假如代入的T是3B或5B...那我這只能說錯得離譜...所以我才說不知道該怎麼修改了...畢竟要啟動建構函式就必須要用到new T
結論~若是想要一次delete是否有辦法呢?...答案是可以的~只是~就我目前的腦力思考~就沒有辦法啟動建構函式了...因為要使用new char....char是標準的1B...只要乘上指標大小跟T類型的大小即可
如下template<class T> T** new_new( int xx , int yy )
{
        T **temp = (T**)(new char[ xx * sizeof(T*) + xx * yy * sizeof(T) ]);        //[ 指標數量 + 大小 ]
        T *buf = (T*)( temp + xx ) ;        //強制轉為1維陣列 + 略過指標數量
       
        temp[ 0 ] = buf;
        for( int i = 1 ; i < xx ; i++ )
        {
                temp[ i ] = temp[ i - 1 ] + yy ;
        }
        return temp;
}如此~只宣告一次new....刪除時自然也只要一次delete...<div class='locked'><em>瀏覽完整內容,請先 <a href='member.php?mod=register'>註冊</a> 或 <a href='javascript:;' onclick="lsSubmit()">登入會員</a></em></div>

kiwis 發表於 2016-11-28 03:43 PM

本帖最後由 kiwis 於 2016-11-28 04:42 PM 編輯

我猜你是想要”只 new 一次,也只 delete 一次,但是想用二維陣列的方式操作”吧?

如果是的話,也不是不行這樣做,只是有點複雜。
重點是要實作一個一維陣列的template。template<class T>
class T2DArray
{
public:
        T2DArray(int new_x, int new_y):
          p_array(0),
          x(new_x),
          y(new_y)
        {
                p_array = new T[ x*y ];
        };

        ~T2DArray()
        {
                if(p_array)
                        delete [] p_array;
        };

        class TArray
        {
        public:
                TArray(T* array, int new_length):
                  length(new_length),
                  p_array(array)
                {
                }

                T& operator[](int index)
                {
                        if(index >= length || 0 > index)
                                throw std::invalid_argument( "invalid index.\n" );
                        else
                                return (p_array);
                };

        private:
                T* p_array;
                int length;

        };

        TArray operator[](int index)
        {
                if(index >= x || 0 > index)
                        throw std::invalid_argument( "invalid index.\n" );
                else
                        return TArray(p_array+(y * index) ,y);
        };


private:
        T2DArray(const T2DArray&);                           // copy constructor
        T2DArray& operator=(const T2DArray&);        // assign operator
        T* p_array;
        int x;
        int y;

};

T2DArray[ index ] 會傳回一個 TArray,
TArray[ index ] 會傳回記憶體上真正的數字,

不過是一個感覺上危險到不行的東西,
隨時會來個 Array Out Of Bound 的例外...

個人並不推薦這種用法。...<div class='locked'><em>瀏覽完整內容,請先 <a href='member.php?mod=register'>註冊</a> 或 <a href='javascript:;' onclick="lsSubmit()">登入會員</a></em></div>

kiwis 發表於 2016-11-28 03:49 PM

補一下實際操作,不知道為何程式碼沒辦法貼,所以請看附檔 : p

inunu 發表於 2016-11-29 04:12 AM

baepi 發表於 2016-11-28 10:17 AM static/image/common/back.gif
感謝還是有人給予回覆...其實我測試到現在已經半放棄了
delete要幾次跟幾維本質上是不太有直接關係的~是 ...

一般的二維陣列用法因為涉及兩個 [], (兩層的指標)
所以需要額外 new[] 分配第一層的指標陣列
除非你像 kiwis 大把整個 [] 的作用取代
若沒有規定必需使用 [] 去存取某個項目的話
我可能會寫個 getter function 用參數代入 x, y
因為實作簡單, 偵錯容易
就像你說的乘一乘算 index, 和檢查有沒有超過而已

malloc() 是只分配空間, 不初始物件
而用 new 要考慮的是物件的初始
實際空間分配反而不太需要考慮 (往往也很難判斷)
你之前的使用邏輯有點偏向 malloc()
沒考慮到不同的類別有自己的初始方式
一次性的用 new[] 混在一起會變不倫不類...
(用 T 的初始方式一起去初始 T* 指標?)...<div class='locked'><em>瀏覽完整內容,請先 <a href='member.php?mod=register'>註冊</a> 或 <a href='javascript:;' onclick="lsSubmit()">登入會員</a></em></div>

kilean 發表於 2016-11-29 02:34 PM

其實,我不太明白你的用意。
用多少就配置多少記憶體,new 了卻不用,只是為了避開錯誤,不是正確的作法。
靜態動態一維二維陣列對記憶體的使用上你可能要去了解。
包含過去的我在內,很多人對這方面觀念很弱,真的不好解釋。
試著解釋給其他同事聽,也是解釋了很久。

基本上:
1. 我不建議在function 或method 內new 東西然後 return 出來。
我的習慣上則是在class中分別在Create 與 Destroy處理。

2. 我盡可能的會使用一維陣列。

3.若認為使用上有些困擾的話,會使用 property,但不是每個環境都能支援。
#include <iostream>
using namespace std;

#define COUNT_X 3
#define COUNT_Y 2

template<class T>
class MyArray
{
private:
    int ACol;
    int ARow;
    T **Array;   
public:
    MyArray(int x, int y);
    ~MyArray();

        T* GetArray(int x, int y);
   
};

template<class T>
MyArray<T>::MyArray(int x, int y)
{
    ACol=x;
    ARow=y;
    Array=new T*;
    for(int i=0; i<x; i++)
    {
        Array=new T;
    }
}

template<class T>
MyArray<T>::~MyArray()
{
    for(int i=0; i<ACol; i++)
    {
        delete [] Array;
    }
    delete [] Array;
}
template<class T>
T* MyArray<T>::GetArray(int x, int y)
{
    return &Array;
}

//----

template<class T>
class MyArray2
{
private:
    int ACol;
    int ARow;
    T *Array;
   
public:
    MyArray2(int x, int y);
    ~MyArray2();

    T* GetArray(int x, int y);
};

template<class T>
MyArray2<T>::MyArray2(int x, int y)
{
    ACol=x;
    ARow=y;
    Array=new T;
}

template<class T>
MyArray2<T>::~MyArray2()
{
    delete [] Array;
}

template<class T>
T* MyArray2<T>::GetArray(int x, int y)
{
    return &Array;
}

//----

class test
{
public:
        int a;
        int b;
        int index;
        test();
        ~test();
};
test::test()
{
        cout << "這是建構子" <<endl;
        a = 7;
        b = 8;
        index=0;
}
test::~test()
{
        cout << "這是解構子" << index <<endl;
}

int main(int argc, char** argv)
{
       
               
        cout << "靜態二維陣列" << endl;
        test t;
        for( int i = 0 ; i < COUNT_X ; i++ )
        {
               
                for( int j = 0 ; j < COUNT_Y ; j++ )
                {
                        cout << t[ i ][ j ].a << t[ i ][ j ].b << "\t";
                        t.index=1;
                }
                cout << endl;
        }
        cout << endl;
        

               
        cout << "動態二維陣列" << endl;
        test **tt;
        tt = new test*[ COUNT_X ];
        for( int i = 0 ; i < COUNT_X ; i++ )
        {
                tt = new test[ COUNT_Y ];
        }
        for( int i = 0 ; i < COUNT_X ; i++ )
        {
               
                for( int j = 0 ; j < COUNT_Y ; j++ )
                {
                        cout << tt[ i ][ j ].a << tt[ i ][ j ].b << "\t";
                        tt.index=2;
                }
                cout <<endl;
        }
        for( int i = 0 ; i < COUNT_X ; i++ )
        {
                delete [] tt;
        }
        delete [] tt;
                cout << endl;

        cout << "靜態物件一(二維)" << endl;
        MyArray<test> t3(COUNT_X, COUNT_Y);
        for(int i=0; i<COUNT_X; i++)
        {
            
            for(int j=0; j<COUNT_Y; j++)
            {
                cout << t3.GetArray(i,j)->a << t3.GetArray(i,j)->b << "\t";
                t3.GetArray(i,j)->index=3;
            }
            cout <<endl;
        }
        cout << endl;
        
        cout << "動態物件一(二維)" <<endl;
        MyArray<test> *t4=new MyArray<test>(COUNT_X, COUNT_Y);
        for(int i=0; i<COUNT_X; i++)
        {
            
            for(int j=0; j<COUNT_Y; j++)
            {
                cout << t4->GetArray(i,j)->a << t4->GetArray(i,j)->b << "\t";
                t4->GetArray(i,j)->index=4;
            }
            cout << endl;
        }
        delete t4;
        cout << endl;

        cout << "靜態物件二(一維)" << endl;
        MyArray2<test> t5(COUNT_X, COUNT_Y);
        for(int i=0; i<COUNT_X; i++)
        {
            
            for(int j=0; j<COUNT_Y; j++)
            {
                cout << t5.GetArray(i,j)->a << t5.GetArray(i,j)->b << "\t";
                t5.GetArray(i,j)->index=5;
            }
            cout << endl;
        }
        cout << endl;
        
        cout << "動態物件二(一維)" << endl;
        MyArray2<test> *t6=new MyArray2<test>(COUNT_X, COUNT_Y);
        for(int i=0; i<COUNT_X; i++)
        {
            
            for(int j=0; j<COUNT_Y; j++)
            {
                cout << t6->GetArray(i,j)->a << t6->GetArray(i,j)->b << "\t";
                t6->GetArray(i,j)->index=6;
            }
            cout <<endl;
        }
        delete t6;
                cout << endl;
       
        return 0;
}
執行結果
靜態二維陣列
這是建構子
這是建構子
這是建構子
這是建構子
這是建構子
這是建構子
78      78
78      78
78      78

動態二維陣列
這是建構子
這是建構子
這是建構子
這是建構子
這是建構子
這是建構子
78      78
78      78
78      78
這是解構子2
這是解構子2
這是解構子2
這是解構子2
這是解構子2
這是解構子2

靜態物件一(二維)
這是建構子
這是建構子
這是建構子
這是建構子
這是建構子
這是建構子
78      78
78      78
78      78

動態物件一(二維)
這是建構子
這是建構子
這是建構子
這是建構子
這是建構子
這是建構子
78      78
78      78
78      78
這是解構子4
這是解構子4
這是解構子4
這是解構子4
這是解構子4
這是解構子4

靜態物件二(一維)
這是建構子
這是建構子
這是建構子
這是建構子
這是建構子
這是建構子
78      78
78      78
78      78

動態物件二(一維)
這是建構子
這是建構子
這是建構子
這是建構子
這是建構子
這是建構子
78      78
78      78
78      78
這是解構子6
這是解構子6
這是解構子6
這是解構子6
這是解構子6
這是解構子6

這是解構子5
這是解構子5
這是解構子5
這是解構子5
這是解構子5
這是解構子5
這是解構子3
這是解構子3
這是解構子3
這是解構子3
這是解構子3
這是解構子3
這是解構子1
這是解構子1
這是解構子1
這是解構子1
這是解構子1
這是解構子1
...<div class='locked'><em>瀏覽完整內容,請先 <a href='member.php?mod=register'>註冊</a> 或 <a href='javascript:;' onclick="lsSubmit()">登入會員</a></em></div><br><br><br><br><br><div></div>

baepi 發表於 2016-11-29 03:45 PM

kilean 發表於 2016-11-29 02:34 PM static/image/common/back.gif
其實,我不太明白你的用意。
用多少就配置多少記憶體,new 了卻不用,只是為了避開錯誤,不是正確的作法。
...

其實昨天看完kiwis大的寫法後就有想到大大的構思並完成了...只是建構二維我跟3樓大大的寫法一致(應該也是大眾的寫法)~因為保持記憶體連貫性...無論在運算速度上還是使用memset上
但若是單單只採用一維而不架構二為陣列指標(如大大的class 2)則會耗時在那運算上....相信我~速度真的有差~~~...<div class='locked'><em>瀏覽完整內容,請先 <a href='member.php?mod=register'>註冊</a> 或 <a href='javascript:;' onclick="lsSubmit()">登入會員</a></em></div>

kiwis 發表於 2016-11-30 06:20 PM

kiwis 發表於 2016-11-28 03:43 PM static/image/common/back.gif
我猜你是想要”只 new 一次,也只 delete 一次,但是想用二維陣列的方式操作”吧?

如果是的話,也不是不 ...

>kaworucloud
的確每操作一次 T2DArray[] 會產生一個暫時性的物件(8 byte),不過為了保證 assign 的操作,沒辦法用 static singleton 的方式。例如:T2DArray = T2DArray;

其實最簡單的方式就是自己計算 pointer 一次要跳多少是最有效率的。...<div class='locked'><em>瀏覽完整內容,請先 <a href='member.php?mod=register'>註冊</a> 或 <a href='javascript:;' onclick="lsSubmit()">登入會員</a></em></div>
頁: [1]