討論區快速選單
知識庫快速選單
網路投保旅行平安險 掌握Salesforce雲端管理秘訣 傑米的攝影旅遊筆記
[ 回上頁 ] [ 討論區發言規則 ]
如何判斷某一指標指向單一物件和矩陣?
更改我的閱讀文章字型大小
作者 : north780316(north)
[ 貼文 5 | 人氣 0 | 評價 0 | 評價/貼文 0 | 送出評價 1 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/18 上午 11:46:12
假設如下兩pointer:

int *ptr1 = new int[10]();
int *ptr2 = new int();

cout << typeid(ptr1).name() << endl
作者 : north780316(north)
[ 貼文 5 | 人氣 0 | 評價 0 | 評價/貼文 0 | 送出評價 1 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/18 上午 11:54:27
不好意思,剛剛沒有打完就按到enter...

int *ptr1 = new int[10]();
int *ptr2 = new int();

cout << typeid(ptr1).name() << endl
    << typeid(ptr2).name() << endl;

兩者的輸出結果是一樣的,

因為現在想要做一個smart pointer的樣板
想要做的如一般pointer的功能有
但是我要銷毀smart pointer 所指的記憶體時
該如何做到判別該記憶體為單一物件還是矩陣呢?
因為如果用delete [] 銷毀單一物件會發生error
希望可以有一方法可以判別該smart pointer指向單一物件或矩陣後
可以決定要使用delete或delete []
附上C++的程式碼
希望高手幫忙~謝謝!!

//Smart pointer, use-count class
#ifndef SMARTPOINTER_H
#define SMARTPOINTER_H

#include <iostream>

using namespace std;

template <typename T> class SmartPtr;

template <typename T>
class U_ptr {

    friend class SmartPtr<T>;

    static unsigned U_ptr_count;

    private :

     U_ptr (T *p) : ip(p), use(1) { }

     ~U_ptr (){ delete ip; }//希望這邊可以判別ip指向單一物件還是矩陣後決定要用delete ip 或 delete [] ip

     T *ip;

     unsigned use;
};

template <typename T>
unsigned U_ptr<T>::U_ptr_count = 0;

template <typename T>
class SmartPtr {

    public :

     SmartPtr () : ptr(NULL) { }
     SmartPtr (T *p) : ptr(new U_ptr<T>(p)) { ptr->U_ptr_count++;}
     SmartPtr (const SmartPtr<T> &rhs) : ptr(rhs.ptr) { ++ptr->use; }

     ~SmartPtr () { if(ptr && --ptr->use == 0) delete ptr; }

     T & operator* () { return *ptr->ip; }
     T & operator[](const unsigned &index) { return *(ptr->ip + index); }
     T * operator->() { return ptr->ip; }

     T * operator= (const SmartPtr<T> &rhs)
     {
     if (ptr) {
     ++rhs.ptr->use;
     if (--ptr->use == 0)
     delete ptr;
     ptr = rhs.ptr;

     } else {
     ++rhs.ptr->use;
     ptr = rhs.ptr;
     }
     return ptr->ip;
     }

     unsigned use_count () { return ptr->use; }
     static unsigned U_ptr_count () { return U_ptr<T>::U_ptr_count; }

    private :

     U_ptr<T> *ptr;
};

#endif
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/18 下午 12:39:00
你說的矩陣應是指陣列吧

基本上 c++ 的單一物件指標和物件陣列的指標是沒分別的(C++ 在這方面很有問題)
若你要做 Smart pointer 應只對單一物件, 對於物件陣列就用像個陣列的容器來包裝後再放入 Smart pointer 中

我有在做超讚的 Smart pointer , 你可來 http://cxxlman.co.cc/blog/ 看看, 因才剛改版,說明文件還沒修改,不過看看還是可知用法
作者 : north780316(north)
[ 貼文 5 | 人氣 0 | 評價 0 | 評價/貼文 0 | 送出評價 1 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/18 下午 01:15:49
回樓上~
謝謝你的回答!
的確如果用容器代替陣列的話就沒有這個問題了!
但是希望可以把這個smart pointer用在一般陣列上的話
有辦法去判別該用delete或delete[]嗎?(不另外增加smart pointer的成員或初始引數和不另寫class的前提下)
因為希望一般pointer做得到的smart pointer都要做得到~
再請高手幫忙...謝啦!!


作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/18 下午 04:12:00
override new/new[],多配置一點memory做註記。
最後也要 override delete/delete[],才能正確的釋放記憶體。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/18 下午 10:13:05
coco 的做法可行,具體的做法如下

#include <iostream>

using namespace std;


class Object
{

public:
 char tag; // 0 表示單一物件, 1 表示物件陣列
 virtual ~Object(){}

 void *operator new(size_t s)
 {
  void *tmp = new char[s];
  ((Object *)tmp)->tag = 0;
  return tmp;
 }

 void *operator new[](size_t s)
 {
  void *tmp = new char[s];
  ((Object *)tmp)->tag = 1;
  return tmp;
 }

 void operator delete(void *m,size_t s)
 {
  if(((Object *)m)->tag == 0)
   ::delete (char*)m;
  else
   ::delete [](Object *)m;
 }
};

class A:virtual public Object
{
 int i;
public:
 virtual ~A()
 {
  cout << "A::~A()" << endl;
 }
};

int main()
{
 A *pA = new A;
 A *pAarray = new A[5];

 cout << "delete pA" << endl;
 delete pA;

 cout << endl << "delete pAarray" << endl;
 delete pAarray;

 return 0;
}
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/19 上午 12:48:47
#include <iostream>
using namespace std;

class Object
{
public:
  char tag; // 0 表示單一物件, 1 表示物件陣列
  virtual ~Object(){}

  void *operator new(size_t s)
  {
    void *tmp = new char[s];
    ((Object *)tmp)->tag = 0;
    return tmp;
  }

  void *operator new[](size_t s)
  {
    void *tmp = new char[s];
    ((Object *)tmp)->tag = 1;
    return tmp;
  }
};

class A:virtual public Object
{
  int i;
public:
  virtual ~A()
  {
    cout << "A::~A()" << endl;
  }
};

int main()
{
  A *pA = new A;
  A *pAarray = new A[5];

//在 Smart pointer 內做以下判斷
  cout << "delete pA" << endl;
  if(pA->tag == 0)
    delete pA;
  else
delete [] pA;

  cout << endl << "delete pAarray" << endl;
  if(pAarray->tag == 0)
    delete pAarray;
  else
delete [] pAarray;


  return 0;
}
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/19 上午 01:04:46
按太快

上面第一個例子不正確,不能在父類別上做陣列操作。
只好用第二個例子代替
作者 : sleepyfish(愛睏魚) C#優秀好手C++優秀好手貼文超過500則
[ 貼文 524 | 人氣 0 | 評價 2890 | 評價/貼文 5.52 | 送出評價 13 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/19 上午 11:12:18

int *ptr2 = new int[1]();
取代掉
int *ptr2 = new int();

就可達成型別的一致性了。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/19 上午 11:28:34
>class Object
>{
>public:
> char tag; // 0 表示單一物件, 1 表示物件陣列
> virtual ~Object(){}
>
> void *operator new(size_t s)
> {
> void *tmp = new char[s];
> ((Object *)tmp)->tag = 0;
> return tmp;
> }

"多配置一點memory做註記"我的意思是暗地裡做記號。
在類別裡多一個成員,除了秘密暴光外,陣列配置時時,
每一個陣列元素都有一個tag也是太浪費。
我的意思大概是像這樣:
class Object
{
public:
 void *operator new(size_t sz)
 {
void *p = :: operator new(sz+sizeof(bool));
*(bool *)p= false;//true是陣列,false不是陣列
return p+sizeof(bool);
}
new []依樣畫葫蘆,這樣這個標記不但不可見,陣列也只多配置一個bool的記憶體。
delete時,只要把指標移原配置地址,就可以參考到這個標記和釋放記憶體。


作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/19 下午 12:18:59

>用
>int *ptr2 = new int[1]();
>取代掉
>int *ptr2 = new int();
>
>就可達成型別的一致性了。

如果只是要解決 delete 的用法是可以這樣做, 但 Smart pointer 要面對的議題蠻多的, 比如若要轉型時, 還是得知道指標所指物件不是陣列才可以轉型, 其他像物件是否共享, 若是共享還會牽扯到 delete 的時機和循環參照等議題
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/19 下午 12:32:32

>>class Object
>>{
>>public:
>> char tag; // 0 表示單一物件, 1 表示物件陣列
>> virtual ~Object(){}
>>
>> void *operator new(size_t s)
>> {
>> void *tmp = new char[s];
>> ((Object *)tmp)->tag = 0;
>> return tmp;
>> }
>
>'多配置一點memory做註記'我的意思是暗地裡做記號。
>在類別裡多一個成員,除了秘密暴光外,陣列配置時時,
>每一個陣列元素都有一個tag也是太浪費。
>我的意思大概是像這樣:
>class Object
>{
>public:
> void *operator new(size_t sz)
> {
>void *p = :: operator new(sz+sizeof(bool));
>*(bool *)p= false;//true是陣列,false不是陣列
>return p+sizeof(bool);
>}
>new []依樣畫葫蘆,這樣這個標記不但不可見,陣列也只多配置一個bool的記憶體。
>delete時,只要把指標移原配置地址,就可以參考到這個標記和釋放記憶體。

這我是有想過, 只是這多配置的空間是要放前面還是後面, 若放後面, operator delete 不知要怎麼取得, 若放前面不確定 c++ 在指標的前頭是否會另有作為而干擾到,

不過最重要的一點是 operator delete 是不是每層 class 都要覆寫, 還是嫌麻煩用一個 class Object::operator delete 就想解決, 但這樣又會牽扯到父類別不能做物件陣列的 delete(vc 例外), 所以還是用 class Object 做明顯的標記再和 Smart pointer 配合會比較實際

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/19 下午 03:04:25
>若放前面不確定 c++ 在指標的前頭是否會另有作為而干擾到,
不會!
operator new返回的是 raw memory(全新的,沒有人使用)。
資料放前面,但返回的指標已移開,所以資料不會再被使用到(駭客程式除外)。
反而用tag在operator new[]是大有問題的,因為raw memory
不一定是new[]所得到指標,通常new[]會在從operator new[]得到
的memory的前面加工(放置陣列元素大小),然後再返回往下移的指標。
(跟我的方法雷同,看吧,大家都這樣搞呢^^)
另外要注意new 和 operator new是不一樣的,operator new是new喚起
的運算子。所以重載operator new的函式裡不能再呼叫 new。
雖然你是用 new char[],已經違規了(雖然char的建構沒做什麼事)。
> void *tmp = new char[s];
> ((Object *)tmp)->tag = 0;
這裡的tmp不一定是new 得到指標(見下個topic說明)
所以((Object *)tmp)->tag = 0會有問題。


>不過最重要的一點是 operator delete 是不是每層 class 都要覆寫,
> 還是嫌麻煩用一個 class Object::operator delete 就想解決
只是加工令可判斷是否為陣列物件的話,只要覆寫子類別的operator new即可。
但再次提醒,重載operator new 要麼呼叫::operator new,不然就用malloc或其它
raw memory 的配置,不可以使用::new。
同樣的重載operator delete也是要用::operator delete 或 free,不可以使用
::delete。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/19 下午 04:30:51
>只是加工令可判斷是否為陣列物件的話,只要覆寫子類別的operator new即可。
更正:
只是加工令可判斷是否為陣列物件的話,只要覆寫"基"類別的operator new即可。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/19 下午 08:30:04

>>若放前面不確定 c++ 在指標的前頭是否會另有作為而干擾到,
>不會!
>operator new返回的是 raw memory(全新的,沒有人使用)。
>資料放前面,但返回的指標已移開,所以資料不會再被使用到(駭客程式除外)。
>反而用tag在operator new[]是大有問題的,因為raw memory
>不一定是new[]所得到指標,通常new[]會在從operator new[]得到
>的memory的前面加工(放置陣列元素大小),然後再返回往下移的指標。
>(跟我的方法雷同,看吧,大家都這樣搞呢^^)
>另外要注意new 和 operator new是不一樣的,operator new是new喚起
>的運算子。所以重載operator new的函式裡不能再呼叫 new。
>雖然你是用 new char[],已經違規了(雖然char的建構沒做什麼事)。
>> void *tmp = new char[s];
>> ((Object *)tmp)->tag = 0;
>這裡的tmp不一定是new 得到指標(見下個topic說明)
>所以((Object *)tmp)->tag = 0會有問題。

忘了應該用 ::new 和 ::delete, 謝謝提醒

你說 new[] 得到的會往下位移, 我試了一下, 真的會內, 那不管是用明顯的 tag 還是你的暗地裡做, 都不能解決問題, 除非這個位移量有標準規定

>
>>不過最重要的一點是 operator delete 是不是每層 class 都要覆寫,
>> 還是嫌麻煩用一個 class Object::operator delete 就想解決
>只是加工令可判斷是否為陣列物件的話,只要覆寫子類別的operator new即可。
>但再次提醒,重載operator new 要麼呼叫::operator new,不然就用malloc或其它
>raw memory 的配置,不可以使用::new。
>同樣的重載operator delete也是要用::operator delete 或 free,不可以使用
>::delete。

這裡我想錯了, 在呼叫 member operator delete[] 之前會先去呼叫 Destructor 的動作, 這東西不常用, 有點思緒錯亂

現在因為有那個位移,的關西, 我就想不出解決的辦法了
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/19 下午 09:57:50
>忘了應該用 ::new 和 ::delete, 謝謝提醒
::operator new 和 ::operator delete

>你說 new[] 得到的會往下位移, 我試了一下, 真的會內,
>那不管是用明顯的 tag 還是你的暗地裡做, 都不能解決問題,
>除非這個位移量有標準規定
底下的例子是一般做法(不是規定):
class CA;
cCA *pA= new CA[10];
如果CA有重載 operator new的情況,編譯器怎麼實現::new:
void *p= CA::operator new(sizeof(classA)*10+sizeof(size_t));//(1)多配置一點記憶體
*(size_t *)p= 10;//(2)多配置的記憶體用來放元素量
return p+sizeof(size_t);//(3)返回的指標下移(因為多配置的部份要暗藏起來)

再看 CA::operator new怎麼依樣畫葫蘆:
CA::void *operator new(size_t sz)
{
void *p = :: operator new(sz+sizeof(bool));//(1')我也A一點放到私人口袋
*(bool *)p= false;//(2')到了私人口袋的錢當然可以用一用
return p+sizeof(bool);//(3')不要把A到的錢也充公了(交給::new)
}
比較(1)<->(1'), (2)<->(2'), (3)<->(3')
就知道是一樣的A錢觀念,就像封包協議一樣,一層一層加封。
delete 時則反向一層一層拆封一樣,不會有問題。
就像公司的老板要經理從倉庫領五台iPhone4s要做測試。
經理就向倉管要六台iPhone4s;暗槓了一台自已玩,給老板五台交差。
倉管呢,則從倉庫提取了七台;也暗槓了一台回家給老婆玩,給經理六台交差。
老板測試夠了,五台iPhone4s交待經理返給倉庫。
經理就把暗槓的一台加上,變成六台給倉管入庫。
倉管一看,不能再玩下去了,只好向老婆要回那一台iPhome4s,共七台入庫。
結果呢,大家都玩到了,東西也都物歸原地,一切美好!
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/19 下午 10:01:28
訂正:
class CA;
CA *pA= new CA[10];
如果CA有重載 operator new的情況,編譯器怎麼實現::new:
void *p= CA::operator new(sizeof(CA)*10+sizeof(size_t));//(1)多配置一點記憶體
*(size_t *)p= 10;//(2)多配置的記憶體用來放元素量
return p+sizeof(size_t);//(3)返回的指標下移(因為多配置的部份要暗藏起來)
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/19 下午 11:39:02
綜合分析後也只剩顯示的 tag 才行得通,因 new 單一物件並沒有位移,且還得賭上 new [] 只位移 sizeof(size_t) 的量,可能其他編譯器還會加料也說不定


#include <iostream>
using namespace std;

class Object
{
public:
 char tag; // 0 表示單一物件, 1 表示物件陣列
 virtual ~Object(){}

 void *operator new(size_t s)
 {
  char *tmp = ::new char[s];
  ((Object *)tmp)->tag = 0; // 單一物件沒位移
  return tmp;
 }

 void *operator new[](size_t s)
 {
  char *tmp = ::new char[s];
  ((Object *)tmp+sizeof(size_t))->tag = 1; // 在 gcc 4.6.1 可這麼做
  return tmp;
 }
};

class A:virtual public Object
{
 int i;
public:
 virtual ~A()
 {
  cout << "A::~A()" << endl;
 }
};

int main()
{
 A *pA = new A;
 A *pAarray = new A[5];

 cout << "delete pA" << endl;
 if(pA->tag == 0)
  delete pA;
 else
  delete pA;

 cout << endl << "delete pAarray" << endl;
 if(pAarray->tag == 0)
  delete pAarray;
 else
  delete [] pAarray;

 return 0;
}
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/19 下午 11:41:11
>
> cout << 'delete pA' << endl;
> if(pA->tag == 0)
>  delete pA;
> else
>  delete pA;
>

這裡要修一下

 cout << 'delete pA' << endl;
 if(pA->tag == 0)
  delete pA;
 else
  delete [] pA;
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/19 下午 11:48:53

>class A:virtual public Object
>{
> int i;
>public:
> virtual ~A()
> {
>  cout << 'A::~A()' << endl;
> }
>};
>

修了上面那個還得再修這個, 不過這也明顯看出適用性太低了, 這麼麻煩不如不要用物件陣列比較實在


class A:public Object
{
 int i;
public:
 virtual ~A()
 {
  cout << 'A::~A()' << endl;
 }
};

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
主題發起人north780316註記此篇回應為最佳解答 2012/1/20 上午 07:32:14
以下做個結論,就是目前仍找不到解決之道^^

#include <iostream>
using namespace std;

class Object
{
 char tag; // 0 表示單一物件, 1 表示物件陣列
public:
 virtual ~Object(){}

 bool isArray()
 {
  return tag == 1;
 }

 void *operator new(size_t s)
 {
  char *tmp = ::new char[s];
  ((Object *)tmp)->tag = 0; // 單一物件沒位移
  return tmp;
 }

 void *operator new[](size_t s)
 {
  char *tmp = ::new char[s];
  ((Object *)(tmp+sizeof(size_t)))->tag = 1; // 在 gcc 4.6.1 可這麼做
  return tmp;
 }
};

//class A:virtual public Object
class A:public Object // 這裡沒辦法加 virtual 已失去了 Object 功用
{
 int i;
public:
 virtual ~A()
 {
  cout << "A::~A()" << endl;
 }
};

int main()
{
 A *pA = new A;
 A *pAarray = new A[5];

 cout << "delete pA" << endl;
 if(pA->isArray())
  delete [] pA;
 else
  delete pA;

 cout << endl << "delete pAarray" << endl;
 if(pAarray->isArray())
  delete [] pAarray;
 else
  delete pAarray;

 return 0;
}
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/20 上午 07:38:56
再不然就不要用 operator new 來解決, 而是在放入 Smart pointer 時就明確告訴它是單一物件還是物件陣列
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/20 下午 03:16:29
>以下做個結論,就是目前仍找不到解決之道^^
太悲觀了吧!
本來我想給一個提示就可以了:
override new/new[],多配置一點memory做註記。
看來還是得寫個範例才行:(
我只是做一個最簡便的註記,這只是為了說明可行性,
真正的實現可能還需要有更嚴謹的註記方式。
先假設new一個陣列時,算會多配置一點記憶體
來放置陣列元素數量,這個數量值放在記憶區塊的前端且不可能為0。(註)
那麼我們可以在new一個單一元素時也加註個陣列元素數量為0的標記。
class CA
{
int m_imember;
public:
static Delete(CA *p){
if (*(__int64 *)((__int8 *)p-sizeof(__int64)))
delete [] p;//這是個陣列指標
else
delete p;//是是個單一陣列
}
static void * operator new(size_t sz){
void *p = ::operator new(sz+sizeof(__int64));
*(__int64 *)p= 0;//使用64位元使誤判的機率更小
return (__int8 *)p+sizeof(__int64);
}
static void operator delete(void *p){
if (p){
p=(__int8 *)p-sizeof(__int64);
::operator delete (p);
}
}
};

void main()
{
CA *pA= new CA[10];
CA::Delete(pA);//step into 看看是否能正確的delete[]pA
pA= new CA;
CA::Delete(pA)//;step into 看看是否能正確的deletepA
system("pause");
}

註:我看過的C++編譯器都是這樣搞new[]的,因為delete[]時要能從陣列指標lookup到元素數量,
除了這個方法也佷難找到其它簡便的了(有其它方法,但都會複雜化)。所以誤判的機率已經很低很低了。
若還是要抱怨,就自己做一個更嚴謹的標記來確保萬無一失。
另一個課題,C++不保證new一個非陣列物件,返回的指標一定是operator new所配置的那一個。
(和陣列一樣,可能有位移)
但真要吹毛求疵,還是輕易可以克服,可以用一個評估函式,計算operator new的配置和實際new
返回的位移量。把這個位移量加入CA:Delete裡面計算就可以了(應該不用我再寫範例了吧)

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/20 下午 08:10:24

> if (*(__int64 *)((__int8 *)p-sizeof(__int64)))
> delete [] p;//這是個陣列指標


你還沒把 繼承 的情況考量進去, 若要你的做法能成功, 每層 class 都要照你方法重複寫一次,
若只在 CA 寫, 則 "delete [] p;" 這個在標準上就過不了關了

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/20 下午 08:27:02
>你還沒把 繼承 的情況考量進去, 若要你的做法能成功, 每層 class 都要照你方法重複寫一次,
>若只在 CA 寫, 則 "delete [] p;" 這個在標準上就過不了關了
::只是要加註記,和類別無關啊!(前面提過了)
你是說像這樣的繼承嗎?
class CA
{
int m_imemberA;
public:
static Delete(CA *p){
if (*(__int64 *)((__int8 *)p-sizeof(__int64)))
delete [] p;
else
delete p;
}
static void * operator new(size_t sz){
void *p = ::operator new(sz+sizeof(__int64));
*(__int64 *)p= 0;
return (__int8 *)p+sizeof(__int64);
}
static void operator delete(void *p){
if (p){
p=(__int8 *)p-sizeof(__int64);
::operator delete (p);
}
}
virtual ~CA(){};
};

class CB: public CA
{
int m_imemberA;
virtual ~CB(){};
};

void main()
{
CB*pB= new CB[10];
CB::Delete(pB);
pB= new CB;
CB::Delete(pB);
system("pause");
}

OK啊!還是有什麼特別繼承有問題,請舉例。

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/20 下午 10:26:10
你忘了厚,delete [] 指令不可以用在父類別,因配置空間不同
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/20 下午 10:51:33
>你忘了厚,delete [] 指令不可以用在父類別,因配置空間不同
oops!!仔細看我的範例。
再看一下原樓主問題:
>//望這邊可以判別ip指向單一物件還是矩陣後決定要用delete ip 或 delete [] ip
C++的delete operator是operator delete是不一樣的。
new delete不能重載,能重載的是operator delete。
operator delete被喚起時,new delete已經做完物解構了,這時候你想做手腳
早來不及了。
所以能做就是在delete之前就要先判斷是要用delete還是delete[]。
不是進入operator delete才判斷是單一物件還是陣列物件。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/20 下午 11:21:30
啊!CA::Delete(CA *p)這個函式確實需要繼承類別重載。
其它的不需要。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/20 下午 11:58:43

>啊!CA::Delete(CA *p)這個函式確實需要繼承類別重載。
>其它的不需要。

你設計一個檢查是不是物件陣列的函數就好, delete 的工作交給 Smart pointer, 就樣就可以不須重載了

現在你的 CA 和我的 Object 要面臨是不是可以被用 virtual 的方式繼承的問題, 若不行,就只能單一繼承, 無法顧全 C++ 複雜的繼承機制

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/21 上午 12:00:46
>啊!CA::Delete(CA *p)這個函式確實需要繼承類別重載。
CA::Delete需要虛擬函式,如果連這個函式也懶得重載
(或怕忘了重載而出包),也可以做一個小巨集解決:
#define SMARTDELETE(p) \
if (p->IsArray(p)) delete []p; \
else delete p;

class CA
{
int m_imemberA;
public:
static bool IsArray(void *p){
return *(__int64 *)((__int8 *)p-sizeof(__int64))?true:false;
}
static void * operator new(size_t sz){
void *p = ::operator new(sz+sizeof(__int64));
*(__int64 *)p= 0;
return (__int8 *)p+sizeof(__int64);
}
static void operator delete(void *p){
if (p){
p=(__int8 *)p-sizeof(__int64);
::operator delete (p);
}
}
virtual ~CA(){
m_imemberA=0;
};
};

class CB: public CA
{
int m_imemberA;
public:
virtual ~CB(){
m_imemberA=0;
};
};

void main()
{
CB *pB= new CB[10];
SMARTDELETE(pB);
pB= new CB;
SMARTDELETE(pB);
system("pause");
}

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/21 上午 12:13:48
hmm...這樣寫比較好:
#define SMARTDELETE(p) \
if (p->IsArray()) delete []p; \
else delete p;

class CA
{
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
bool IsArray(){
return *(__int64 *)((__int8 *)this-sizeof(__int64))?true:false;
}
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/21 上午 01:07:36

>
>現在你的 CA 和我的 Object 要面臨是不是可以被用 virtual 的方式繼承的問題, 若不行,就只能單一繼承, 無法顧全 C++ 複雜的繼承機制

這才是要點吧
假設

class CK:public CA{};
class CL:public CA{};
class CM:public CK, public CL{};

CM *pcm = new CM;
CK *pck = new CM;
CL *pcl = new CM;

delete pcm; // 這個可能沒問題
delete pck; // 這個可能沒問題
delete pcl; // 這個可能有問題




作者 : north780316(north)
[ 貼文 5 | 人氣 0 | 評價 0 | 評價/貼文 0 | 送出評價 1 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/21 上午 01:12:36
謝謝樓上兩位高手幫忙~^^

獲益良多~謝謝!
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/21 上午 01:38:55
>class CK:public CA{};
>class CL:public CA{};
>class CM:public CK, public CL{};
>CM *pcm = new CM;
>CK *pck = new CM;
>CL *pcl = new CM;
>delete pcm; // 這個可能沒問題
>delete pck; // 這個可能沒問題
>delete pcl; // 這個可能有問題
這個沒問題啊(若有重載operator delete,只要有虛擬解構子lookup),
你是指這樣吧:
CL *pcl = new CM[];
delete[] pcl; // 這個可能有問題
這在之前討論過,這是C++規定(有它的道理),也無關於樓主的問題。
樓主的問題在於是否可以判斷new出來的指標是不是array。
我提出的標記法,實際上已解決樓主的問題。
只是我犯了一個錯,多事的把delete operator放在目標指標的類別成員內。
除了使用macro把delete opertor提出來外,像樓主貼的程式,把delete operator
寫在目標指標的container內也不會有問題。
再強調一次delete[] pcl;本來pcl的型態就一定要和建立的型態一致。
和樓主的問題本質無關。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/21 上午 06:30:44

>>delete pcm; // 這個可能沒問題
>>delete pck; // 這個可能沒問題
>>delete pcl; // 這個可能有問題

抱歉 這裡我表達得太草率 改一下

SMARTDELETE(pcm); // 這個可能沒問題
SMARTDELETE(pck); // 這個可能沒問題
SMARTDELETE(pcl); // 這個可能有問題
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/21 上午 10:11:50
>class CK:public CA{};
>class CL:public CA{};
>class CM:public CK, public CL{};
>CL *pcl = new CM;
>SMARTDELETE(pcl); // 這個可能有問題
抱歉,我也沒注意,你是指多重繼承的問題嗎?
多重繼承基類別有同一個基基類別,不管基基類別是不是CA都會有問題。
所以和CA或我的標記法無關。
多重繼承在C++是用虛擬繼承解決的:
class CK:virtual public CA{};
class CL:virtual public CA{};
class CM:public CK, public CL{};
CL *pcl = new CM;
SMARTDELETE(pcl); // 這樣就沒有問題了
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/21 下午 01:42:19

>>class CK:public CA{};
>>class CL:public CA{};
>>class CM:public CK, public CL{};
>>CL *pcl = new CM;
>>SMARTDELETE(pcl); // 這個可能有問題
>抱歉,我也沒注意,你是指多重繼承的問題嗎?
>多重繼承基類別有同一個基基類別,不管基基類別是不是CA都會有問題。
>所以和CA或我的標記法無關。
>多重繼承在C++是用虛擬繼承解決的:
>class CK:virtual public CA{};
>class CL:virtual public CA{};
>class CM:public CK, public CL{};
>CL *pcl = new CM;
>SMARTDELETE(pcl); // 這樣就沒有問題了

在 gcc 4.6.1 CA 的 this 指標不會是在你預計的位置上了,vc 應該也一樣

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/21 下午 05:10:09
>在 gcc 4.6.1 CA 的 this 指標不會是在你預計的位置上了,vc 應該也一樣
我在VC6上試是對的,而且這個問題我已經預先說明過了:

>>另一個課題,C++不保證new一個非陣列物件,返回的指標一定是operator new所配置的那一個。
>>(和陣列一樣,可能有位移)
>>但真要吹毛求疵,還是輕易可以克服,可以用一個評估函式,計算operator new的配置和實際new
>>返回的位移量。把這個位移量加入CA:Delete裡面計算就可以了(應該不用我再寫範例了吧)

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/21 下午 05:30:45

>>在 gcc 4.6.1 CA 的 this 指標不會是在你預計的位置上了,vc 應該也一樣
>我在VC6上試是對的,而且這個問題我已經預先說明過了:

class CB:virtual public CA

在記憶體配置上會比較像

class CB
{
  CA mCA;
};

你不能假設 mCA 的位置一定是在 CB 的起頭,當然這得視編譯器如何做,不過這屬黑箱處理,把註記位置以 mCA 位置做參考本來就具有不確定性,至少 gcc 證明了這一點,因此我才下結論目前法解決樓主的須求


作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/21 下午 08:37:04
>class CB:virtual public CA
>在記憶體配置上會比較像
>class CB
>{
 > CA mCA;
>};
>你不能假設 mCA 的位置一定是在 CB 的起頭,當然這得視編譯器如何做,
>不過這屬黑箱處理,把註記位置以 mCA 位置做參考本來就具有不確定性,
>至少 gcc 證明了這一點,因此我才下結論目前法解決樓主的須求
我並不做這樣的假設。
CB *pB= new CB;
pB得到的指標會和CA::operator new返回的指標有一個offset(可能為0)。
即使多重繼承的 CD *pD= new CD;也有一樣的offset。
這個offset在編譯器是固定的,我不敢說絕對不會變,但若會變,編譯器會搞死自己。
(若真的會變,還是可解決,就是動態計算offset,只是一樣會搞得較複雜)
因為pB可得,而CA::operator new配置的位置也可知,所以這個offset是可算出來的。
這就是可行性的基礎。
本來我就不太想寫範例,沒有好好測試就容易有bug,而把bug當成不可行的論據,這樣
的討論就會沒完沒了。
測試我的範例有問題,為什麼不先看看是bug還是理論有缺陷?
我最後的範例確實有bug,那個CA::IsArray還是要用static才對。
(突發想美化它反而陷入錯誤),糾正一下:
#define SMARTDELETE(p) \
if (CA::IsArray(p)) delete []p; \
else delete p;

class CA
{
int m_imemberA;
public:
static bool IsArray(void *p){
__int64 i64Array= *(__int64 *)((__int8 *)p-sizeof(__int64));
return *(__int64 *)((__int8 *)p-sizeof(__int64))?true:false;

}
static void * operator new(size_t sz){
void *p = ::operator new(sz+sizeof(__int64));
*(__int64 *)p= 0;
return (__int8 *)p+sizeof(__int64);
}
static void operator delete(void *p){
if (p){
p=(__int8 *)p-sizeof(__int64);
::operator delete (p);
}
}
virtual ~CA(){
m_imemberA=0;
};
};

class CB: virtual public CA
{
int m_imemberB;
public:
virtual ~CB(){
m_imemberB=0;
};
};
class CC: virtual public CA
{
int m_imemberC;
public:
virtual ~CC(){
m_imemberC=0;
};
};
class CD: public CB, public CC
{
int m_imemberD;
int mmm[100];
public:
virtual ~CD(){
m_imemberD=0;
};
};

void main()
{
CD *pD= new CD[10];
SMARTDELETE(pD);
pD= new CD;
CA::IsArray(pD);
SMARTDELETE(pD);
system("pause");
}

若這個範例沒有再犯錯,而在你的gcc測試不成功,那一定是那個offset我沒計入的關係。
這個範例假設offset是0。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/21 下午 08:40:44
static bool IsArray(void *p){
__int64 i64Array= *(__int64 *)((__int8 *)p-sizeof(__int64));//這一行是供debug時查看參考值對不對
return *(__int64 *)((__int8 *)p-sizeof(__int64))?true:false;

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/21 下午 09:31:30
以下程式你跑跑看,用了 virtual 位置會偏掉
但那個 i 若拿掉位置都不會偏

#include <iostream>

using namespace std;

class A
{
 int i; // 若沒有這個位置都不會偏
public:
 void Show()
 {
  cout << "A this\t\t" << std::hex << this << endl;
  cout << "A::i\t\t" << std::hex << &(this->i) << endl;
 }

public:
 void * operator new(size_t sz)
 {
  void *tmp = new char[sz];
  cout << "A::new()\t" << std::hex << tmp << endl;
  return tmp;
 }
};

class B:public A
{
public:
 void Show()
 {
  A::Show();
  cout << "B this\t\t" << std::hex << this << endl;
 }
};

class C:virtual public A
{
public:
 void Show()
 {
  A::Show();
  cout << "C this\t\t" << std::hex << this << endl;
 }
};


int main()
{
 cout << "沒用 virtual ..." << endl;
 B *pB = new B;
 pB->Show();

 cout << endl << "加了 virtual ..." << endl;
 C *pC = new C;
 pC->Show();

 delete pB;
 delete pC;
 return 0;
}
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/21 下午 09:39:06
有虛擬函數位置都會偏

#include <iostream>

using namespace std;

class A
{
  int i;
public:
  void Show()
  {
    cout << "A this\t\t" << std::hex << this << endl;
    cout << "A::i\t\t" << std::hex << &(this->i) << endl;
  }

public:
  void * operator new(size_t sz)
  {
    void *tmp = new char[sz];
    cout << "A::new()\t" << std::hex << tmp << endl;
    return tmp;
  }
};

class B:public A
{
public:
  void Show()
  {
    A::Show();
    cout << "B this\t\t" << std::hex << this << endl;
  }
  virtual int f()
  {
    int i = 0;
    return i++;
  }
};

class C:virtual public A
{
public:
  void Show()
  {
    A::Show();
    cout << "C this\t\t" << std::hex << this << endl;
  }
  virtual int f()
  {
    int i = 0;
    return i++;
  }
};


int main()
{
  cout << "沒用 virtual ..." << endl;
  B *pB = new B;
  pB->Show();

  cout << endl << "加了 virtual ..." << endl;
  C *pC = new C;
  pC->Show();

  delete pB;
  delete pC;
  return 0;
}
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/21 下午 09:52:06
經多種組合測試
只要 A 沒有任何成員變數, 位置幾乎都不會偏, 也許你的方法可行
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/21 下午 11:30:50
>經多種組合測試
>只要 A 沒有任何成員變數, 位置幾乎都不會偏, 也許你的方法可行
CA 不需要資料成員,但不能沒有虛擬解構子,否則就成了癈物。
C++的物件實體就是資料空間(虛擬函式也是資料)。
虛擬繼承主要是資料虛擬(vtable也是資料),子類別物件不是被include而
是改用指向基類實體代替。這個操作可能需要多配置一些記憶體來存資訊。
(看編譯器的作法)
CD *pD= new CD;
無論CD是否虛擬繼承自CA,pD-(CA::operator new 返回值)應是一個固定offset。
這個固定offset可由pD的container計算而得,在Delete函式裡面加人計算,才是可行
的做法。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/22 上午 06:26:48

>>經多種組合測試
>>只要 A 沒有任何成員變數, 位置幾乎都不會偏, 也許你的方法可行
>CA 不需要資料成員,但不能沒有虛擬解構子,否則就成了癈物。
>C++的物件實體就是資料空間(虛擬函式也是資料)。
>虛擬繼承主要是資料虛擬(vtable也是資料),子類別物件不是被include而
>是改用指向基類實體代替。這個操作可能需要多配置一些記憶體來存資訊。
>(看編譯器的作法)
>CD *pD= new CD;
>無論CD是否虛擬繼承自CA,pD-(CA::operator new 返回值)應是一個固定offset。
>這個固定offset可由pD的container計算而得,在Delete函式裡面加人計算,才是可行
>的做法。

那麼只要 CA 不含資料成員+虛擬解構子, 在任何情況的應用下位置都不會偏掉(最好是有標準的保證), 那麼你算是解決了樓主的難題了
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/22 下午 03:18:19
>那麼只要 CA 不含資料成員+虛擬解構子, 在任何情況的應用下位置都不會偏掉(最好是有標準的保證),
沒有保證...
>那麼你算是解決了樓主的難題了
不算吧!樓主的ip沒有說 不含資料成員+虛擬解構子或其它虛擬成員。
但,你唸茲再茲的"位置偏掉"的問題,我一直強調,那是可以解決的。
當然解決的方法看你要到多嚴格,程式複雜度也會不同。
好吧,看起來再不寫個範例還是不行。
不過,我還是不打算寫太嚴格的範例,所以有幾個適用的前題:
1.編譯器new [] operator至少會在operator new 配置的記憶體前加註陣列元素數量。
2.陣列元素數量不可能為0
有這兩個前題,我的範例就可以不管operator new[] 和 operator delete[]這一塊。
所以我還是延用前面的CA範例來改寫:
#define SMARTDELETE(p) \
if (CA::IsArray(p)) delete []p; \
else delete p;

class CA
{
static int m_iOffset;
public:
static bool IsArray(void *p){
return *(__int64 *)((__int8 *)p-m_iOffset)?true:false;\\注意這裡改用CA::m_iOffset
}
static void * operator new(size_t sz){
void *p = ::operator new(sz+sizeof(__int64));
*(__int64 *)p= 0;
return (__int8 *)p+sizeof(__int64);
}
static void operator delete(void *p){
if (p)
::operator delete ((__int8 *)p-sizeof(__int64));
}
virtual ~CA(){
};
friend class COffsetA;
};
int CA::m_iOffset;

class COffsetA: public CA
{//class COffsetA和其唯一的global物件的功能,就是算出CA::m_iOffset
static void * m_pAllocOperatorNew;
public:
enum{EvaluateOffsetOfCA};
operator int(){
return m_iOffset;
}
static void * operator new(size_t sz){
m_pAllocOperatornew = ::operator new(sz+sizeof(__int64));
*(__int64 *)m_pAllocOperatorNew= 0;
return (__int8 *)m_pAllocOperatorNew+sizeof(__int64);
}
COffsetA(){}
COffsetA(int i){
COffsetA *p= new COffsetA;
m_iOffset= (__int8 *)p-(__int8 *)m_pAllocOperatorNew;//m_iOffset就是唸茲再茲"徧掉"的offset
delete p;
}
}OffsetOfCA(COffsetA::EvaluateOffsetOfCA);
void * COffsetA::m_pAllocOperatorNew;

class CB: virtual public CA
{
int m_imemberB;
public:
virtual ~CB(){
m_imemberB=0;
};
};
class CC: virtual public CA
{
int m_imemberC;
public:
virtual ~CC(){
m_imemberC=0;
};
};
class CD: public CB, public CC
{
int m_imemberD;
public:
virtual ~CD(){
};
};
//可以試試這裡的main或自行撰寫其它測試在各種的編譯器中是否正確:
void main()
{
CD *pD;
pD= new CD[10];
SMARTDELETE(pD);
pD= new CD;
SMARTDELETE(pD);
system("pause");
}
我相信絕大部份的編譯器應該可行的。若還有不成功的編譯器,只要把標註演算
加強(可能operator new[]/operator delete[]也需重載),就可使適用性再加強。
問題是"加註標記"法,你要做到什麼樣的程度,不是可不可行的問題。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/22 下午 08:01:58

>>那麼你算是解決了樓主的難題了
>不算吧!樓主的ip沒有說 不含資料成員+虛擬解構子或其它虛擬成員。

樓主也沒 CA 這種東西, 或說所有元件都是自己寫的, 但只要 CA 確定可行, 也就是不含資料成員的 CA, 那麼這個 CA 可用在自己寫的程式上, 也可用在別人寫的, 如下

class LL: virtual public CA, public KK
{
};

KK 是別人寫的元件
那麼用這方法就可以把 LL 放入 Smart pointer, 也等於可以把 KK 放入 Smart pointer

>但,你唸茲再茲的'位置偏掉'的問題,我一直強調,那是可以解決的。

我說的位置偏掉是不定位置的意思, 會造成偏掉是因使用 virtual 繼承 CA 的關西, 不是可計算的, 雖然依測試似乎都固定偏了 4 bytes, 但你的計算是依據什麼?

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/22 下午 11:08:21
除夕夜還在想這個問題啊!
先拜早年吧,恭喜發財,C++功力更上層樓。
>樓主也沒 CA 這種東西, 或說所有元件都是自己寫的, 但只要 CA 確定可行,
>也就是不含資料成員的 CA, 那麼這個 CA 可用在自己寫的程式上, 也可用在別人寫的, 如下

>{
>};
CA 若不能有虛擬解構子(虛擬成員也是資料),那麼繼承者也不能有虛擬解構子.
虛擬解構子在C++是非常重要的,正常的程式,99%的類別
應該都要有虛擬解構子。把CA的虛擬解構子拿掉,它的武功就癈了。

>我說的位置偏掉是不定位置的意思,
對同一個編譯器來說,位移應該是固定的。
因為不固定,編譯器自己要lookup回去原始點會都會很麻煩。
編譯器不會沒事自找麻煩。
若是真有這樣的編譯器,而CA還是想能適用它,還是辦得到。
那就是CA子類別的container要個別做COffsetA所做的事。
不能用一個global的OffsetOfCA一體適用。
程式當然麻煩一點,但正如我說的,看你要做到什麼程度。

>會造成偏掉是因使用 virtual 繼承 CA 的關西, 不是可計算的,
>雖然依測試似乎都固定偏了 4 bytes, 但你的計算是依據什麼?
我並不預測它偏了 4 bytes。
我們想要知道什麼?
不就是operator new 配置的記憶體位置和new operator返回位置
的位移嗎?
而這兩個值都是可知的,怎麼會不能計算位移呢?
我的範例不是正確的計算了它嗎?
唯一的問題是CB和CC都繼承自CA,那麼new CB和new CC的位移
保證一樣嗎?我的程式假設是一樣的(理由上一段已說了),但若不一
樣還是可解,解法上一段也提出了。
這樣還有什麼理由一定要癈了CA的武功呢?
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/23 上午 08:21:43

我先說明一下什麼 virtual 繼承,它主要的目的是讓被使用 virtual 繼承的基底類別在整個物件中只有一個,因 C++ 有多重繼承,所以有可能發生以下的情況

class A{};
class B:public A{};
class C:public A{};
class D:public B,public C{};

這時 D 的物件將有兩個 A,這並沒有錯,要看使用上的須求,若 A 是希望在整個物件上只有一份,就得用 virtual 繼承,這是一般概念,但落實到 C++ 是怎麼辦到的,看看以下例子

class A
{
public:
 A(int i)
 {

 }
};

class B:virtual public A
{
public:
 B(int i)
  :A(i)
 {

 }
};

class C:public B
{
public:
 C(int i)
  :A(i),B(i) // A 須在這被初始化
 {

 }
};

C++ 的做法很直接,把 A 強迫變成其下每一個類別的父類別,但只由最終類別負責初始化,如果你去追蹤 B 對 A 的初始化能力被拿掉了

所以對 C 而言相當於

class C:public B,public A
{
public:
 C(int i)
  :A(i),B(i)
 {

 }
};

所以對於 A 在整個物件的記憶體配置上不應看成一定會排在最前面,所以我對 A 位置偏掉的說法改一下,改成 "A 的 this 指標無法確定會在哪"

只是實測在某個條件下 gcc 會把 A 放在最前頭,雖然不應看成標準,但多找些主流的編譯器試試若都是這樣,可以當成密笈^^

另外樓主對物件陣列判斷這項須求,以我個人經驗,除了很簡單的資料結構外,幾乎沒用過,一般都是用容器比較安全,像 c# 的物件陣列和 C++ 的做法不同,C# 也是用容器來做的(應該可這樣說吧)
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/23 下午 01:45:00
>所以對於 A 在整個物件的記憶體配置上不應看成一定會排在最前面,
>所以我對 A 位置偏掉的說法改一下,改成 "A 的 this 指標無法確定會在哪"
能不能把思路抽回到原點?想得太複雜容易人火入魔。
到了CA::operator new和CA::operator delete已經和類別無關了。
所以兩個運算都不能有this的觀念(之前不是糾正你在CA::operator new不能操作成員嗎),
至於A的this在那裡也不是標記法要關心的對象(你越想關心,問題就越變越複雜)。
仔細在看看我的程式,有用到A的this嗎?
CA::IsArray也不用this(之前那個非static版本,說過是一個錯誤)。
它只是把傳進來的指標,例用offset算回原標記位置,就這樣而已。
和類別,繼承 ,this都無關。
"傳進來的指標"是什麼?就是new operator取得的指標,而offset就是
new operator到new operator配置指標的差值。這裡面那裡有用到this?
至於虛擬繼承的CA實體會被locate在那裡,根本也不是需要關心的事。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/23 下午 01:54:51
訂正:
"傳進來的指標"是什麼?就是new operator取得的指標,而offset就是
new operator到operator new配置指標的差值。這裡面那裡有用到this?

補充:
事實上::operator new可能會去呼叫malloc來配置記憶體,
malloc通常也是會多配置一點空間來做一些標記(allocated header)。
不然,free memory時怎麼知道原配置的大小?
所以標記法在你new一個物件時,已經到處都在發生。
如果不可行,那麼整個記憶體管就都崩潰了。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/23 下午 03:30:55

static bool IsArray(void *p){
__int64 i64Array= *(__int64 *)((__int8 *)p-sizeof(__int64));
return *(__int64 *)((__int8 *)p-sizeof(__int64))?true:false;

原來你用 void* 喔, 可是這樣做你會陷入另一項麻煩, 一個多重繼承的物件, 各層的位置可不一定一樣, 你只是把一個難題換成另一個難題而已
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/23 下午 04:59:12
>原來你用 void* 喔, 可是這樣做你會陷入另一項麻煩, 一個多重繼承的物件,
>各層的位置可不一定一樣, 你只是把一個難題換成另一個難題而已
跟類別繼承沒有關係啦!
只要呼叫IsArray(*p)傳進來的p是當初new出來的地址就沒問題。
(管它怎麼繼承的,offset就是根據new出來的地址去計算的)
那如果不是會怎樣?神仙也難救。
就像是 p= new CD[10];
delete 是你一定要用 delete [] p;//p是當初new出來的地址
所以傳進IsArray的地址也一定要是這個p,這不是我的標計法刁難。
就像你不能用 delete [] &p[1];來代替一樣。
&p[1]不是初new出來的地址,神仙也難救
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/23 下午 09:53:08
如果樓主只是想要在自己的程式中, 避免使用了錯誤的 delete/delete[], 你可以即使在 new 單一物件時, 也用:
  ptr = new int[1]();

銷毀就可以一律用 delete[]. 對比較不複雜的專案來說, 這應該足夠了.

如果真的有複雜的需求, 可以考慮 boost 提供的 smart pointer classes (除非你自己有多的時間想練功 smart pointer class).

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/23 下午 10:02:21

>>原來你用 void* 喔, 可是這樣做你會陷入另一項麻煩, 一個多重繼承的物件,
>>各層的位置可不一定一樣, 你只是把一個難題換成另一個難題而已
>跟類別繼承沒有關係啦!
>只要呼叫IsArray(*p)傳進來的p是當初new出來的地址就沒問題。
>(管它怎麼繼承的,offset就是根據new出來的地址去計算的)
>那如果不是會怎樣?神仙也難救。
>就像是 p= new CD[10];
>delete 是你一定要用 delete [] p;//p是當初new出來的地址
>所以傳進IsArray的地址也一定要是這個p,這不是我的標計法刁難。
>就像你不能用 delete [] &p[1];來代替一樣。
>&p[1]不是初new出來的地址,神仙也難救

樓主是要做 Smart pointer, 當然也要考慮到轉型的須求, 不然就不 smart 了
我也比較同意 R大 建議用 boost 之類的, Smart pointer 不是簡單的課題
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/23 下午 11:24:11
>樓主是要做 Smart pointer, 當然也要考慮到轉型的須求, 不然就不 smart 了
轉型可以啊!但不能用轉型的指標delete吧!不然delete[]怎麼辦(這已經是基本觀念)?
而且這在樓主的應用也不是問題:
U_ptr{
:::::::::::::::::::::::::::::::::::::::::
~U_ptr (){SMARTDELETE(ip); }//刪除ip是回到這裡來刪,不是嗎?那有什麼問題?
T *ip;//這個T不應該是被轉型的型態
};
一律使用陣列的指標雖可達到刪除時的一致性,但若是smart pointer給別人使用的類別,風險性更高。
再說,如果連T都被轉型,那麼R大的"一律陣列法"會更加悽慘(即使沒有多重繼承也會崩潰)。
大部份解決難題的方法,都會有其優點和限制。重要的目的達到了,而沒有太不合理的副作用就是可行。
標記法在解決樓主smart pointer提出的難題之可行性,不是在提出一個完美無缺的方法。

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/23 下午 11:36:50
>再說,如果連T都被轉型,那麼R大的"一律陣列法"會更加悽慘(即使沒有多重繼承也會崩潰)。
糾正自己這一句話,如果連T都被轉型 ,不論怎麼delete都會崩潰。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/24 上午 06:36:36

>>樓主是要做 Smart pointer, 當然也要考慮到轉型的須求, 不然就不 smart 了
>轉型可以啊!但不能用轉型的指標delete吧!不然delete[]怎麼辦(這已經是基本觀念)?

檢查是不是陣列再轉型就行了

>而且這在樓主的應用也不是問題:
>U_ptr{
>:::::::::::::::::::::::::::::::::::::::::
>~U_ptr (){SMARTDELETE(ip); }//刪除ip是回到這裡來刪,不是嗎?那有什麼問題?
>T *ip;//這個T不應該是被轉型的型態
>};

那個 ip 沒辦法保證指向正確的位址 可能只是多重繼承中某一個 class 的位址

以下是我融會貫通後想出來的方法 應是可行的方法

#include <iostream>

using namespace std;

// 要求須以 virtual 來繼承
class Object
{
 bool boolArray;
public:

 template <typename T>
 // 要求 T 以 this 為參數,一定是由最終的 class 來初始化 Object,所以 this 一定正確
 Object(T *ChildSelf)
 {
  // 把註記複製過來
  boolArray = *(((bool*)ChildSelf) - sizeof(size_t) - sizeof(bool));
 }

 virtual ~Object() = 0; // 終於發現解構子 =0 的用途

 bool isArray()
 {
  return boolArray;
 }

 void *operator new(size_t s)
 {
  // 最前頭是註記,加上 new 陣列時會用到的空間
  char *tmp = ::new char[s + sizeof(size_t) + sizeof(bool)];
  *(bool *)tmp = false; // 註記不是陣列
  return tmp + sizeof(size_t) + sizeof(bool);
 }

 void *operator new[](size_t s)
 {
  // 最前頭是註記
  char *tmp = ::new char[s + sizeof(bool)];
  *(bool *)tmp = true; // 註記是陣列
  return tmp + sizeof(bool);
 }

 void operator delete(void *m,size_t s)
 {
  ::delete (((char*)m) - sizeof(size_t) - sizeof(bool));
 }

 void operator delete[](void *m,size_t s)
 {
  ::delete (((char*)m) - sizeof(bool));
 }

};

Object::~Object()
{

}

class A:virtual public Object
{
public:
 A():Object(this) // 一定是由最終的 class 來初始化 Object,所以 this 一定正確
 {

 }
 ~A()
 {
  cout << "A::~A()" << endl;
 }
};



int main()
{
 A *pA = new A;
 A *pAarray = new A[5];

 cout << "delete pA" << endl;
 if(pA->isArray())
  delete [] pA;
 else
  delete pA;

 cout << endl << "delete pAarray" << endl;
 if(pAarray->isArray())
  delete [] pAarray;
 else
  delete pAarray;

 return 0;
}
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/24 上午 10:49:40
>template <typename T>
>// 要求 T 以 this 為參數,一定是由最終的 class 來初始化 Object,所以 this 一定正確
這不是就是我的意思嗎?
>>template <typename T> class SmartPtr;//<--這個T不應該是被轉型的型態
>>::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
>>U_ptr{
>>:::::::::::::::::::::::::::::::::::::::::
>>~U_ptr (){SMARTDELETE(ip); }//刪除ip是回到這裡來刪,不是嗎?那有什麼問題?
>>T *ip;//這個T不應該是被轉型的型態
>>};


> if(pAarray->isArray())
>  delete [] pAarray;
> else
>  delete pAarray;
如果pAarray是被轉型的指標,這樣就不會有問題嗎?
C++裡只要你想用delete[]來刪陣列,目標指標的型態就一定要正確的。
樓主既然要求判斷ip是不是array來決定是否使用delete[],就已隱含表示
模板參數T必需不是轉型的型態。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/24 上午 11:21:04

>>template <typename T>
>>// 要求 T 以 this 為參數,一定是由最終的 class 來初始化 Object,所以 this 一定正確
>這不是就是我的意思嗎?

因你的 IsArray() 設計如下, 那個 p 可不一定會是正確位址, 而我的做法是在建構初始化時, 利用正確的位址先把註記複製出來, 以後就不用再去管位址正不正確了

static bool IsArray(void *p){
__int64 i64Array= *(__int64 *)((__int8 *)p-sizeof(__int64));
return *(__int64 *)((__int8 *)p-sizeof(__int64))?true:false;

}


>> if(pAarray->isArray())
>>  delete [] pAarray;
>> else
>>  delete pAarray;
>如果pAarray是被轉型的指標,這樣就不會有問題嗎?

轉型也是 Smart pointer 的設計重點, 可以在轉型時檢查來源是不是陣列再轉, 大概如下做法

template <typename K, typename T>
Smart_Ptr<K> Smart_Cast(const Smart_Ptr<T> Src)
{
  if(Src->isArray())
   return NULL;

  return dynamic_cast<K*>(Src);
}

>C++裡只要你想用delete[]來刪陣列,目標指標的型態就一定要正確的。
>樓主既然要求判斷ip是不是array來決定是否使用delete[],就已隱含表示
>模板參數T必需不是轉型的型態。

Smart pointer 必須要設計成能包裝一個物件中任何一層的 class
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/24 下午 01:38:56
唉,剛剛回的文章不見了...
不想再打那麼多字了(討論也夠多了),就簡單一點的說。

我並不是在討論完善的smart pointer該有什麼功能,
而是就樓主的問題,提出可行的方向:
>template <typename T>
>class U_ptr {
>::::::::::::::::::::::::::::::::::::::::::
>~U_ptr (){ delete ip; }//希望這邊可以判別ip指向單一物件還是矩陣後決定要用delete ip 或 delete [] ip
>T *ip;
>T *ip;
如果T可能是降轉類別,那麼也不用決定要不要用delete[],不是嗎?
問題已被前題毁了,還需想下去嗎?

不管怎麼做要解決陣列或多重繼承降轉的問題,終需在某個地方給出未降轉前正確的型別。
只是程式要設計在那裡而已,好像也不必太多的討論。

另外,程式設計有一個很重要的順序原則要遵守,例如物件未建構前不可以操作成員。
像你的方法:
class Object
{
 bool boolArray;
:::::::::::::::::::::::::::::::::::::::::::::
 void *operator new(size_t s)
 {
  // 最前頭是註記,加上 new 陣列時會用到的空間
  char *tmp = ::new char[s + sizeof(size_t) + sizeof(bool)];
  *(bool *)tmp = false; //<--這裡操作了成員
  return tmp + sizeof(size_t) + sizeof(bool);
 }
::::::::::::::::::::::::::::::::::::::::::::::::::::
};
尚未建構完成就操作成員會怎樣?不可預期結果。
例如,你把boolArray填入某個值,但返回new operator仍可能
被某特定值覆蓋(這在debug mode常會做)
所以,若你想完善標記法,標記還是得在物件之外的容器上做。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/24 下午 08:51:25

>
>我並不是在討論完善的smart pointer該有什麼功能,
>而是就樓主的問題,提出可行的方向:
>>template <typename T>
>>class U_ptr {
>>::::::::::::::::::::::::::::::::::::::::::
>>~U_ptr (){ delete ip; }//希望這邊可以判別ip指向單一物件還是矩陣後決定要用delete ip 或 delete [] ip
>>T *ip;
>>T *ip;
>如果T可能是降轉類別,那麼也不用決定要不要用delete[],不是嗎?
>問題已被前題毁了,還需想下去嗎?
>
>不管怎麼做要解決陣列或多重繼承降轉的問題,終需在某個地方給出未降轉前正確的型別。
>只是程式要設計在那裡而已,好像也不必太多的討論。

Smart pointer 至少在表面上要像一般指標一樣, 一個物件可以同時被多個不同介面的指標所指向, 所以一個 Smart pointer 只是包裝一個物件的某一個介面而已, 所以不能把 Smart pointer 看作一定會持有你想要的位址

>
>另外,程式設計有一個很重要的順序原則要遵守,例如物件未建構前不可以操作成員。
>像你的方法:
>class Object
>{
> bool boolArray;
>:::::::::::::::::::::::::::::::::::::::::::::
> void *operator new(size_t s)
> {
>  // 最前頭是註記,加上 new 陣列時會用到的空間
>  char *tmp = ::new char[s + sizeof(size_t) + sizeof(bool)];
>  *(bool *)tmp = false; //<--這裡操作了成員
>  return tmp + sizeof(size_t) + sizeof(bool);
> }
>::::::::::::::::::::::::::::::::::::::::::::::::::::
>};
>尚未建構完成就操作成員會怎樣?不可預期結果。
>例如,你把boolArray填入某個值,但返回new operator仍可能
>被某特定值覆蓋(這在debug mode常會做)
>所以,若你想完善標記法,標記還是得在物件之外的容器上做。
>

這部份看要怎麼做比較合理是可以改, 不會影響整個架構,
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/24 下午 10:34:55
>Smart pointer 至少在表面上要像一般指標一樣, 一個物件可以同時被多個不同介面的指標所指向,
>所以一個 Smart pointer 只是包裝一個物件的某一個介面而已, 所以不能把 Smart pointer
>看作一定會持有你想要的位址
不知道你這裡說的和我們說的降轉類型有什麼關係。
Smart pointer本來就可以降轉類別使用Smart pointer裡面還是原類別啊。
難道你是要把把降轉後的型別再丟入一個Smart pointer?
這是smart pointer的正常應用?
我實在不想多談smart pointer,因為議題太大,也不是本議題囡討論的。

就你的程式來說:
>class Object
>{
> bool boolArray;
>public:
> template <typename T>
> // 要求 T 以 this 為參數,一定是由最終的 class 來初始化 Object,所以 this 一定正確
> Object(T *ChildSelf)
> {
>::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
> }
不也要求 T是最終類別嗎?( 所以我說總有一處必需給負責delete的類別知道最終類別的指標)

再看看原樓主的程式:
>template <typename T>
>class SmartPtr {
> public :
> SmartPtr () : ptr(NULL) { }
> SmartPtr (T *p) : ptr(new U_ptr<T>(p)) { ptr->U_ptr_count++;}
模板傳入的T被用來new U_ptr<T>,所以U_ptr模板參數的T是最終正確的類別,沒錯吧!
所以
>template <typename T>
>class U_ptr {
>::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
> U_ptr (T *p) : ip(p), use(1) { }//<--這裡的p沒有被降轉的T沒錯吧(還是new時的T)?
> ~U_ptr (){ SMARTDELETE(ip);}//<--所以這裡的ip應該是正確的位址,因為ip的型態還是T沒有變
 > T *ip;//ip的型態還是T沒有變
>::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
>};
我覺得沒問題啊,也不用說那裡需要最終的的類別this,因為都是強迫"正確"的完成的。


作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/24 下午 11:20:05
Smart pointer 和一般指標用法是一樣的, 一般指標的用法就如下

class CK:virtual public CA{};
class CL:virtual public CA{};
class CM:public CK, public CL{};

CM *pcm = new CM;
CK *pck = new CM;
CL *pcl = new CM;

或者如下

CM *pcm = new CM;
CK *pck = pcm;
CL *pcl = pck;

只不過 Smart pointer 是由最後放棄持有的那個負責 delete, 如上例萬一放棄持有的是 pcl, 那不就出問題了嗎


作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/25 上午 10:16:49
>CM *pcm = new CM;
>CK *pck = pcm;
>CL *pcl = pck;//<--這個有問題吧?
>只不過 Smart pointer 是由最後放棄持有的那個負責 delete,
> 如上例萬一放棄持有的是 pcl, 那不就出問題了嗎
OK,我沒想CK *pck = pcm;這種情況。
但,還是如我之前提的,需求嚴苛程式就越複雜而已。
基本的方法在,有需求,就見招拆招,修改一下我的範例:
(注意//<--的註解)
class CA
{
static int m_iOffset;
public:
virtual void *GetOrgthis(){return this;}//<--增加這個虛擬函式
void Delete(){//<--Delete取代了原來的SMARTDELETE+IsArray
void *p= GetOrgthis();//<--取得real this
if (*(__int64 *)((__int8 *)GetOrgthis()-m_iOffset))
delete [] this;
else
delete this;
}
static void * operator new(size_t sz){
void *p = ::operator new(sz+sizeof(__int64));
*(__int64 *)p= 0;
return (__int8 *)p+sizeof(__int64);
}
static void operator delete(void *p){
if (p)
::operator delete ((__int8 *)p-sizeof(__int64));
}
virtual ~CA(){
};
friend class COffsetA;
};
int CA::m_iOffset;

class COffsetA: public CA
{
static void * m_pAllocOperatorNew;
public:
enum{EvaluateOffsetOfCA};
operator int(){
return m_iOffset;
}
static void * operator new(size_t sz){
m_pAllocOperatornew = ::operator new(sz+sizeof(__int64));
*(__int64 *)m_pAllocOperatorNew= 0;
return (__int8 *)m_pAllocOperatorNew+sizeof(__int64);
}
COffsetA(){}
COffsetA(int i){
COffsetA *p= new COffsetA;
m_iOffset= (__int8 *)p-(__int8 *)m_pAllocOperatorNew;
delete p;
}
}OffsetOfCA(COffsetA::EvaluateOffsetOfCA);
void * COffsetA::m_pAllocOperatorNew;

class CB: virtual public CA
{
int m_imemberB;
public:
virtual void *GetOrgthis(){return this;}//<--虛擬繼承類別需要覆載GetOrgthis
virtual ~CB(){};
};
class CC: virtual public CA
{
int m_imemberC;
public:
virtual void *GetOrgthis(){return this;}
virtual ~CC(){
m_imemberC=0;
};
};
class CD: public CB, public CC
{
int m_imemberD;
public:
virtual void *GetOrgthis(){return this;};}//<--多重也需繼承類別需要覆載GetOrgthis

virtual ~CD(){
};
};

void main()
{
CD *pD= new CD[10];
pD->Delete();//<--Delete可以正確的刪除陣列
CB *pB= new CB;
CA *pA= pB;
pA->Delete();//<--從從基類物件刪除虛擬繼承的子類物件,OK!
pA= new CD;
pA->Delete();//<--從基類物件刪除多重繼承的子類物件,也是OK!
system("pause");
}

程式增加的麻煩,在於虛擬繼承和多重繼承要覆載GetOrgthis
還好,虛擬繼承和多重繼承應用不多,應該可以接受吧!
事實上,虛擬繼承和多重繼承本來就是爭議性的產物,少用為妙。
有了GetOrgthis , 原來的SMARTDELETE+IsArray就可以用Delete取代。
程式漂亮一些,可稍補GetOrgthis麻煩 ^^

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/25 上午 10:19:36
>void *p= GetOrgthis();//<--取得real this
這一行多餘,用於debug時觀察而已
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/25 下午 03:26:45

>程式增加的麻煩,在於虛擬繼承和多重繼承要覆載GetOrgthis
>還好,虛擬繼承和多重繼承應用不多,應該可以接受吧!
>事實上,虛擬繼承和多重繼承本來就是爭議性的產物,少用為妙。
>有了GetOrgthis , 原來的SMARTDELETE+IsArray就可以用Delete取代。
>程式漂亮一些,可稍補GetOrgthis麻煩 ^^


你的 CA 本來很簡潔,被你改得變難用了,重點是在要放入 Smart pointer 嘛,我把我以前寫的拿出來刪一刪,再加入可辨識陣列的功能(只要物件有支援 isArray() 這個成員函數就行了),所以可配合我上面寫的 Object 使用,你試試 Object 的做法不就很簡單

/******************
** CSMART_PTR.HPP
** 不能用於多線程
** 不能用於循環參照
******************/


#include <stdio.h>

template <class T>
class CSmart_Ptr;

template <class L,class K>
CSmart_Ptr<L> CounterSmart_Cast(CSmart_Ptr<K> &Src);

template <class T>
class CSmart_Ptr
{
 class Bag
 {
  unsigned int BagCounter;

  unsigned int &Counter;
  T *Obj;
 public:
  Bag(T *Obj_Arg) // Constructor
   :Counter(*new unsigned int)
  {
   Counter = 1;
   BagCounter = 1;
   Obj = Obj_Arg;
  }

  Bag(T *Obj_Arg,unsigned int &Counter_Arg) // Constructor
   :Counter(Counter_Arg)
  {
   Counter++;
   BagCounter = 1;
   Obj = Obj_Arg;
  }

  unsigned int &GetCounter()
  {
   return Counter;
  }

  T *GetObj()
  {
   return Obj;
  }

  void IncCounter()
  {
   Counter++;
   BagCounter++;
  }

  void DecCounter()
  {
   if(--Counter == 0)
   {
    if(Obj != NULL && Obj->isArray())
     delete [] Obj;
    else
     delete Obj;
    delete this;
   }
   else if(--BagCounter == 0)
   {
    delete this;
   }
  }
 };

 CSmart_Ptr(T *Src,unsigned int &Counter_Arg) // Copy Constructor
 {
  Bag_Ptr = new Bag(Src,Counter_Arg);
 }

 Bag *Bag_Ptr;
public:
 CSmart_Ptr() // Constructor
 {
  Bag_Ptr = new Bag((T *)NULL);
 }

 CSmart_Ptr(T *Src) // Constructor
 {
  Bag_Ptr = new Bag(Src);
 }

 CSmart_Ptr(const CSmart_Ptr<T> &Src) // Copy Constructor
 {
  Bag_Ptr = Src.Bag_Ptr;
  Bag_Ptr->IncCounter();
 }

 ~CSmart_Ptr()   // Destructor
 {
  if(Bag_Ptr != NULL)
   Bag_Ptr->DecCounter();
 }

 CSmart_Ptr<T> &operator=(const CSmart_Ptr<T> &Src)  // 拷備函數
 {
  Bag_Ptr->DecCounter();
  Bag_Ptr = Src.Bag_Ptr;
  Bag_Ptr->IncCounter();
  return *this;
 }

 bool isNULL()
 {
  return (Bag_Ptr->GetObj() == NULL);
 }

 T *operator->(void)
 {
  return Bag_Ptr->GetObj();
 }

 // 放棄持有的物件
 void Destroy()
 {
  Bag_Ptr->DecCounter();
  Bag_Ptr = NULL;
 }


 template <class L,class K>
 friend CSmart_Ptr<L> CounterSmart_Cast(CSmart_Ptr<K> &Src);
};

/*
** 轉形函數
*/
template <class L,class K>
CSmart_Ptr<L> CounterSmart_Cast(CSmart_Ptr<K> &Src)
{
 if(!Src.isNULL())
 {
  if(Src->isArray())
   return CSmart_Ptr<L>((L *)NULL);

  K *temp1 = Src.operator->();
  L *temp2 = dynamic_cast<L *>(temp1);
  if(temp2 == NULL)
   return CSmart_Ptr<L>((L *)NULL);
  else
  {
   return CSmart_Ptr<L>(temp2,Src.Bag_Ptr->GetCounter());
  }
 }
 else
  return CSmart_Ptr<L>((L *)NULL);
}


作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/25 下午 03:29:56
這是測試範例

#include <iostream>
#include "CSMART_PTR.HPP"

using namespace std;

// 要求須以 virtual 來繼承
class Object
{
 bool boolArray;
public:

 template <typename T>
 // 要求 T 以 this 為參數,一定是由最終的 class 來初始化 Object,所以 this 一定正確
 Object(T *ChildSelf)
 {
  // 把註記複製過來
  boolArray = *(((bool*)ChildSelf) - sizeof(size_t) - sizeof(bool));
 }

 virtual ~Object() = 0; // 終於發現解構子 =0 的用途

 bool isArray()
 {
  return boolArray;
 }

 void *operator new(size_t s)
 {
  // 最前頭是註記,加上 new 陣列時會用到的空間
  char *tmp = ::new char[s + sizeof(size_t) + sizeof(bool)];
  *(bool *)tmp = false; // 註記不是陣列
  return tmp + sizeof(size_t) + sizeof(bool);
 }

 void *operator new[](size_t s)
 {
  // 最前頭是註記
  char *tmp = ::new char[s + sizeof(bool)];
  *(bool *)tmp = true; // 註記是陣列
  return tmp + sizeof(bool);
 }

 void operator delete(void *m,size_t s)
 {
  ::delete (((char*)m) - sizeof(size_t) - sizeof(bool));
 }

 void operator delete[](void *m,size_t s)
 {
  ::delete (((char*)m) - sizeof(bool));
 }

};

Object::~Object()
{

}

class A:virtual public Object
{
public:
 A():Object(this) // 一定是由最終的 class 來初始化 Object,所以 this 一定正確
 {

 }
 ~A()
 {
  cout << "A::~A()" << endl;
 }
};

class B:virtual public Object
{
public:
 B():Object(this) // 一定是由最終的 class 來初始化 Object,所以 this 一定正確
 {

 }
 ~B()
 {
  cout << "B::~B()" << endl;
 }
};


class D:public A,public B
{
public:
 D():Object(this) // 一定是由最終的 class 來初始化 Object,所以 this 一定正確
 {

 }
 ~D()
 {
  cout << "D::~D()" << endl;
 }
};


int main()
{
 CSmart_Ptr<D> D_Ptr = new D;
 CSmart_Ptr<B> B_Ptr = CounterSmart_Cast<B>(D_Ptr); // 多重繼承的轉型

 CSmart_Ptr<D> D_Array = new D[2];
 CSmart_Ptr<B> B_Array = CounterSmart_Cast<B>(D_Array); // 陣列轉型
 if(B_Array.isNULL())
  cout << "避開了陣列轉型" << endl;

 return 0;
}
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/25 下午 04:05:02
>你的 CA 本來很簡潔,被你改得變難用了,重點是在要放入 Smart pointer 嘛,
>我把我以前寫的拿出來刪一刪,再加入可辨識陣列的功能(只要物件有支援 isArray() 這個成員函數就行了),
>所以可配合我上面寫的 Object 使用,你試試 Object 的做法不就很簡單
難就難在IsArray怎麼實現^^
Object的做法,在operator new裡面操作了物件成員boolArray說過不合順序邏輯。
只要有更好的方法,都OK的,討論目的在於可行性,好不好是其次。
話說回來,若原CA很簡潔,後來的CA會難用嗎?
我只是加入一個虛擬成員GetOrgthis,正常的繼承也都不需要再重載它(可以忘了它的存在)。
這樣就會從簡潔變難用?
"不正常的繼承"(虛擬繼承和多重繼承)才需要重載它,而重載佷難嗎?
就把CA的這一行void *GetOrgthis(){return this;}複製就好了,應該也不太費力氣吧。
(何況虛擬繼承和多重繼承不多吧?如果只是給我自己的程式用,我可能懶得理它呢)

不過討論到方法可行,也就可以了,好不好用己不是重點。
若你的Object可以改得合理可行,也是OK。
裡面除了在operator new裡面操作了物件成員需要改進外,
array額外附加的標記也不一定只是size_t的位移,這還是要
寫個類似OffsetOfCA的程式來計算。
反正還是那一句話,方法可行就成了,至於要寫到多完善,就隨人高興了。

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/25 下午 05:42:50

>>你的 CA 本來很簡潔,被你改得變難用了,重點是在要放入 Smart pointer 嘛,
>>我把我以前寫的拿出來刪一刪,再加入可辨識陣列的功能(只要物件有支援 isArray() 這個成員函數就行了),
>>所以可配合我上面寫的 Object 使用,你試試 Object 的做法不就很簡單
>難就難在IsArray怎麼實現^^
>Object的做法,在operator new裡面操作了物件成員boolArray說過不合順序邏輯。
>只要有更好的方法,都OK的,討論目的在於可行性,好不好是其次。
>話說回來,若原CA很簡潔,後來的CA會難用嗎?
>我只是加入一個虛擬成員GetOrgthis,正常的繼承也都不需要再重載它(可以忘了它的存在)。
>這樣就會從簡潔變難用?

當然難用, 因為很難保證會被重載, 在編譯時也沒提醒, 出錯了也找不到原因

>'不正常的繼承'(虛擬繼承和多重繼承)才需要重載它,而重載佷難嗎?
>就把CA的這一行void *GetOrgthis(){return this;}複製就好了,應該也不太費力氣吧。
>(何況虛擬繼承和多重繼承不多吧?如果只是給我自己的程式用,我可能懶得理它呢)

不管是你的 CA 還是我的 Object 都要虛擬繼承, 多重繼承把它看成多介面繼承, 也不算太少見吧

>
>不過討論到方法可行,也就可以了,好不好用己不是重點。
>若你的Object可以改得合理可行,也是OK。
>裡面除了在operator new裡面操作了物件成員需要改進外,
>array額外附加的標記也不一定只是size_t的位移,這還是要
>寫個類似OffsetOfCA的程式來計算。
>反正還是那一句話,方法可行就成了,至於要寫到多完善,就隨人高興了。

把 Object::operator new 改成你 CA 的方法不就行了
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/25 下午 09:35:48
>當然難用, 因為很難保證會被重載, 在編譯時也沒提醒, 出錯了也找不到原因
一再說明我無意寫一個完美的程式,只是提出可行性。
你若有興趣是可以去完善它,不論是我的或你的範例。
提出一些方向,單純的只希望在出錯時提出原因,可以用巨集代替new。
(判斷new出來的指標是否等於GetOrgthis)
或是使用RTTI:
class CA
{
static int m_iOffset;
public:
//virtual void *GetOrgthis(){return this;}//<--有了RTTI這個可以丟了了
void Delete(){
void *p=dynamic_cast<void*>(this);
if (*(__int64 *)((__int8 *)p-m_iOffset))
delete [] this;
else
delete this;
}
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
這樣連GetOrgthis()都可以不必了(回到你說的簡潔的CA,原this不對的問題也消失了)
當然RTTI是要付代價的,如果程式不想付這個代價,那就保留GetOrgthis
只在debug mode時enable RTTI,然後在CA::Delete審核是否有該加
GetOrgthis的類別忘了加:
void CA::Delete(){
ASSERT (dynamic_cast<void*>(this)==this);
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
}
這樣就達成自動偵錯的目的。
這樣子你關心的難用問題解決,提示的改善方向夠多了吧^^
(不想寫範例了,因為已經不是在可行性討論了)

>把 Object::operator new 改成你 CA 的方法不就行了
我不知道那裡不行,我只提可行性方法,其它請見招拆招。
不要抱怨我的程式不完美,本來我連範例都懶得寫的。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/25 下午 09:50:53
訂正:
只在debug mode時enable RTTI,然後在CA::Delete審核是否有該加
GetOrgthis的類別忘了加:
void CA::Delete(){
ASSERT (dynamic_cast<void*>(this)==GetOrgthis());
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
}
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/26 上午 06:25:26

>訂正:
>只在debug mode時enable RTTI,然後在CA::Delete審核是否有該加
>GetOrgthis的類別忘了加:
>void CA::Delete(){
>ASSERT (dynamic_cast<void*>(this)==GetOrgthis());
>:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
>}
>

原來還有 dynamic_cast<void*> 這種用法, 以前還在苦惱找不到這樣功效的方法, 這次討論算是有收獲^^
若能這樣做會變得更簡潔

現只剩 單一物件 位置之前用 0 填補是否可靠, 若有這樣 new CB[0] 用, 會出問題嗎
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/26 下午 11:18:39
>現只剩 單一物件 位置之前用 0 填補是否可靠,
可靠,那個位置配置了,就是in use,沒有泄漏給別人使用,就可安全私底下用。

>若有這樣 new CB[0] 用, 會出問題嗎
new一個陣列元素為0,C++的規定是必需返回一個不為0(或稱有效)的指標。
但該指標不可以dereference(可以解釋為不可以操作物件?)
至於實際配置多少記憶體並沒有規定。
另外我在VC6的經驗是:
p= new CB[0];
那麼CB不能有虛擬構子或重載opertor delete之類的東西。
看起來似乎即使陣列元素為0,在delete時VC還是會從
虛擬解構子去lookup operator delete,但這些東西並沒有attach在虛擬解構子上。
(因為並沒有真正配置和建構物件,所以沒目標可attach)
我不知道是VC6的疏失還是C++標準有其它說明。
或許R大能解疑感...
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/27 上午 01:54:14
>>若有這樣 new CB[0] 用, 會出問題嗎
>new一個陣列元素為0,C++的規定是必需返回一個不為0(或稱有效)的指標。

C++11 允許 failure. 3.7.4.1 Allocation functions

  2. ... Even if the size of the space requested is zero, the request can fail. if the request succeeds, the value returned shall be a non-null pointer value.


>但該指標不可以dereference(可以解釋為不可以操作物件?)

它根本沒有任何物件.


>至於實際配置多少記憶體並沒有規定。

從語言的角度來看, 它是 0 長度的陣列.
從編譯器的 implementation 來看, 應該就是回傳指標之前的那一個記錄長度的區塊.


>另外我在VC6的經驗是:
>p= new CB[0];
>那麼CB不能有虛擬構子或重載opertor delete之類的東西。

什麼叫做「不能有」? 它們導致編譯錯誤嗎?

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/27 上午 07:01:00

>>現只剩 單一物件 位置之前用 0 填補是否可靠,
>可靠,那個位置配置了,就是in use,沒有泄漏給別人使用,就可安全私底下用。

  因有 CB *pCB = new CB[0] 的用法, 會被誤判是單一物件而用 delete pCB 刪除, 正確是要用 delete [] pCB 才對

>>若有這樣 new CB[0] 用, 會出問題嗎
>new一個陣列元素為0,C++的規定是必需返回一個不為0(或稱有效)的指標。

new CB[0] 仍會返回一個指標, 只是原素為 0, 不能做任何事, 只能用 delete [] 刪除

>但該指標不可以dereference(可以解釋為不可以操作物件?)
>至於實際配置多少記憶體並沒有規定。
>另外我在VC6的經驗是:
>p= new CB[0];
>那麼CB不能有虛擬構子或重載opertor delete之類的東西。

因沒有原素可使用, 所以沒有這些問題

另外我覺得我在 Object::operator new 多加一個註記位置並不會有問題, operator new 和 operator delete 原本的用意是要給程式使用自己開發的記憶體管理系統, 所以 C++ 不應去干擾到程式合法使用的空間, 否則記憶體管理系統不就毀了

因你提供 dynamic_cast<void *> 的用法, 我重新改寫 Objec 現在變得更好用, 這應該是最佳解決辦法了

#include <iostream>
#include "CSMART_PTR.HPP"

using namespace std;

// 要求須以 virtual 來繼承
class Object
{

public:

 Object()
 {
 }

 virtual ~Object() = 0; // 終於發現解構子 =0 的用途

 bool isArray()
 {
  void *pTag = (char*)dynamic_cast<void *>(this) - sizeof(size_t) - sizeof(bool);
  return *(bool*)pTag;
 }

 void *operator new(size_t s)
 {
  // 最前頭是註記,加上 new 陣列時會用到的空間
  char *tmp = ::new char[s + sizeof(size_t) + sizeof(bool)];
  *(bool *)tmp = false; // 註記不是陣列
  return tmp + sizeof(size_t) + sizeof(bool);
 }

 void *operator new[](size_t s)
 {
  // 最前頭是註記
  char *tmp = ::new char[s + sizeof(bool)];
  *(bool *)tmp = true; // 註記是陣列
  return tmp + sizeof(bool);
 }

 void operator delete(void *m,size_t s)
 {
  ::delete (((char*)m) - sizeof(size_t) - sizeof(bool));
 }

 void operator delete[](void *m,size_t s)
 {
  ::delete (((char*)m) - sizeof(bool));
 }

};

Object::~Object()
{

}

class A:virtual public Object
{
public:
 A()
 {

 }
 ~A()
 {
  cout << "A::~A()" << endl;
 }
};

class B:virtual public Object
{
public:
 B()
 {

 }
 ~B()
 {
  cout << "B::~B()" << endl;
 }
};


class D:public A,public B
{
public:
 D()
 {

 }
 ~D()
 {
  cout << "D::~D()" << endl;
 }
};


int main()
{
 CSmart_Ptr<D> D_Ptr = new D;
 CSmart_Ptr<B> B_Ptr = CounterSmart_Cast<B>(D_Ptr); // 多重繼承的轉型

 CSmart_Ptr<D> D_Array = new D[2];
 CSmart_Ptr<B> B_Array = CounterSmart_Cast<B>(D_Array); // 陣列轉型
 if(B_Array.isNULL())
  cout << "避開了陣列轉型" << endl;

 return 0;
}
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/27 下午 02:21:00
> 因有 CB *pCB = new CB[0] 的用法, 會被誤判是單一物件而用 delete pCB 刪除,
>正確是要用 delete [] pCB 才對
你是說在CA::Delete裡誤判嗎?
不會new CB[0]是會喚起operator new []不是operator new,所以標記不會錯。
重點是:
 CB *pCB = new CB[0];
pCB->Delete();//<--可以這麼用嗎?
依照C++的說法,不能用,除非Delete是static

>new CB[0] 仍會返回一個指標, 只是原素為 0, 不能做任何事, 只能用 delete [] 刪除
重點在於CB或其基類別有沒有虛擬解構子,若有,那麼依C++的標準是要從
虛擬解構子lookup operator delete。而一個0元素的陣列跟本不會有物件實體,所以
也就沒有vtable可以指向虛擬解構子。
所以在VC6是會發生錯誤的,但找並沒有看過C++說明不可以有虛擬解構子。
而且我覺得這應該可以用一個虛擬物件(裡面只有一個虛擬解構子的vtable)就可以解決。
所以懷疑是VC6本身的漏洞沒處理好。在VC其它版本不會錯嗎?(好奇)

另外
char *tmp = ::new char[s + sizeof(size_t) + sizeof(bool)];
這裡最好用::opeator new(s + sizeof(size_t) + sizeof(bool))
因為operator new 是由new operator喚起的,再呼叫new雖然不是
一定不可以,但總是讓人有階層混亂的感覺。而且,如果你用new來配置,
那麼operator delete是不是應該這麼寫:
void operator delete(void *m,size_t s)
{
    delete [] (((char*)m) - sizeof(size_t) - sizeof(bool));
}




作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/27 下午 05:37:44

>> 因有 CB *pCB = new CB[0] 的用法, 會被誤判是單一物件而用 delete pCB 刪除,
>>正確是要用 delete [] pCB 才對
>你是說在CA::Delete裡誤判嗎?
>不會new CB[0]是會喚起operator new []不是operator new,所以標記不會錯。
>重點是:
> CB *pCB = new CB[0];
>pCB->Delete();//<--可以這麼用嗎?
>依照C++的說法,不能用,除非Delete是static

delete [] pCB 會發現沒元素, 不會去呼叫 Destructor, 但 delete pCB 一定會去呼叫, pCB 所指並無物件, 所以一定會當掉


>
>>new CB[0] 仍會返回一個指標, 只是原素為 0, 不能做任何事, 只能用 delete [] 刪除
>重點在於CB或其基類別有沒有虛擬解構子,若有,那麼依C++的標準是要從
>虛擬解構子lookup operator delete。而一個0元素的陣列跟本不會有物件實體,所以
>也就沒有vtable可以指向虛擬解構子。
>所以在VC6是會發生錯誤的,但找並沒有看過C++說明不可以有虛擬解構子。
>而且我覺得這應該可以用一個虛擬物件(裡面只有一個虛擬解構子的vtable)就可以解決。
>所以懷疑是VC6本身的漏洞沒處理好。在VC其它版本不會錯嗎?(好奇)

delete [] 會檢查有沒有元素再去做那些動作, 所以並不會有問題
你是怎麼做給 CV6 測試的, po 出來看看

>另外
>char *tmp = ::new char[s + sizeof(size_t) + sizeof(bool)];
>這裡最好用::opeator new(s + sizeof(size_t) + sizeof(bool))

在 gcc 沒法 ::opeator new 這樣用
你這 new(s + sizeof(size_t) + sizeof(bool)) 什麼意思看不懂

>因為operator new 是由new operator喚起的,再呼叫new雖然不是
>一定不可以,但總是讓人有階層混亂的感覺。而且,如果你用new來配置,
>那麼operator delete是不是應該這麼寫:
>void operator delete(void *m,size_t s)
>{
> delete [] (((char*)m) - sizeof(size_t) - sizeof(bool));
>}
>
嗯 是要用 delete [] 才對
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/27 下午 09:17:44
>delete [] pCB 會發現沒元素, 不會去呼叫 Destructor,
不會呼叫Destructor,還是得喚起相應的operator delete
new CB[0]不是會喚起CA::operator new[]嗎?
那裡配置的就應應該由那裡釋放。
所以delete []pCB當然也要喚起CA::operator delete[]不是嗎?
OK,想一想編譯器正常要怎麼決定呼叫operator delete[]。
由C++的說明若CB沒有虛擬解構子,比較簡單,就由子類別往基類別
找operator delete[],找到就使用,沒找到到就用global的。
若有虛擬解構子呢?C++規定要由虛擬解構子lookup operator delete[],
找不到就用global的。問題來了,
pCB根本連一個物件都沒有,沒有物件那來vtable來放虛擬解構子指標?
沒有虛擬解構子怎麼進到解構子 lookup operator delete[]?
我說了這是可以解決的,最簡單的就是在元素為0時,需要構造一個基本的的vtable
(至少要有解構子指標)

>delete [] 會檢查有沒有元素再去做那些動作, 所以並不會有問題
>你是怎麼做給 CV6 測試的, po 出來看看
如前說明,還是得要呼叫operator delete,範例就隨便寫就可以:
class AA
{
int m_i;
public:
static void * operator new[](size_t sz){
void *p= ::operator new (sz);
return p;
}
static void operator delete [] (void *p){
::operator delete(p);
}
virtual ~AA(){
m_i=0;
}
};

void main()
{
AA *p= new AA[0];
delete [] p;//這裡在VC6會有問題,把~AA的virtual修飾子去掉就可以
}


>在 gcc 沒法 ::opeator new 這樣用
Oh打錯字,是::operator new 和 ::operator delete
gcc不可能沒有啊,它們是C++標準程式館函式。
重載operator new和operator delete就是重載它們啊!
這些allocate函式已經是C++語言的一部份了,是不是少include
什麼header file了?抱歉gcc我只用過幾次。
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/27 下午 10:34:44
>>delete [] pCB 會發現沒元素, 不會去呼叫 Destructor,
>不會呼叫Destructor,還是得喚起相應的operator delete
>new CB[0]不是會喚起CA::operator new[]嗎?

應該要. 但根據我的測試, VC++ (2010) 並沒有:

  #include <iostream>
  #include <cstdlib>

  void* operator new(size_t n)
  {
    std::cout << "::new" << std::endl;
    return malloc(n);
  }

  void* operator new[](size_t n)
  {
    std::cout << "::new[]" << std::endl;
    return malloc(n);
  }

  void operator delete(void *p)
  {
    std::cout << "::delete" << std::endl;
    return free(p);
  }

  void operator delete[](void *p)
  {
    std::cout << "::delete[]" << std::endl;
    return free(p);
  }

  struct B
  {
    void* operator new(size_t n)
    {
      std::cout << "B::new" << std::endl;
      return malloc(n);
    }
    void* operator new[](size_t n)
    {
      std::cout << "B::new[]" << std::endl;
      return malloc(n);
    }
    void operator delete(void* p)
    {
      std::cout << "B::delete" << std::endl;
      free(p);
    }
    void operator delete[](void* p)
    {
      std::cout << "B::delete[]" << std::endl;
      free(p);
    }

    virtual ~B()
    {
      std::cout << "B::~B" << std::endl;
    }
  };

  struct D : public B
  {
    void* operator new(size_t n)
    {
      std::cout << "D::new" << std::endl;
      return malloc(n);
    }
    void* operator new[](size_t n)
    {
      std::cout << "D::new[]" << std::endl;
      return malloc(n);
    }
    void operator delete(void* p)
    {
      std::cout << "D::delete" << std::endl;
      free(p);
    }
    void operator delete[](void* p)
    {
      std::cout << "D::delete[]" << std::endl;
      free(p);
    }

    virtual ~D()
    {
      std::cout << "D::~D" << std::endl;
    }
  };

  int main()
  {
    D* d = new D[0];
    delete[] d;
  }

VC++ 的執行結果:
  ::new[]
  ::delete[]

再用 gcc 4.5.0 來編譯, 得到的是:
  D::new[]
  D::delete[]

這個應該比較正確.


>那裡配置的就應應該由那裡釋放。
>所以delete []pCB當然也要喚起CA::operator delete[]不是嗎?


>OK,想一想編譯器正常要怎麼決定呼叫operator delete[]。
>由C++的說明若CB沒有虛擬解構子,比較簡單,就由子類別往基類別
>找operator delete[],找到就使用,沒找到到就用global的。
>若有虛擬解構子呢?C++規定要由虛擬解構子lookup operator delete[],
>找不到就用global的。問題來了,
>pCB根本連一個物件都沒有,沒有物件那來vtable來放虛擬解構子指標?
>沒有虛擬解構子怎麼進到解構子 lookup operator delete[]?
>我說了這是可以解決的,最簡單的就是在元素為0時,需要構造一個基本的的vtable
>(至少要有解構子指標)

這個問題根本不存在, delete[] 是個陣列格式, C++ 規定, 陣列格式的 delete, static type 跟 dynamic type 必須一樣. Lookup 只適用於非陣列格式的 delete.

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/27 下午 10:53:16
>這個問題根本不存在, delete[] 是個陣列格式, C++ 規定, 陣列格式的 delete, static type
>跟 dynamic type 必須一樣. Lookup 只適用於非陣列格式的 delete.
也對...我想歪了。
因為在VC6,只要new一個有虛擬解構子,元素為0的陣列,刪除物件就會掛掉。
我直覺的往那個方向去想(忘了delete陣列,物件型態本來就不能轉型)。
但如果不是這個原因,就不知道VC6那裡犯錯了。

作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/28 上午 12:27:23
>也對...我想歪了。
>因為在VC6,只要new一個有虛擬解構子,元素為0的陣列,刪除物件就會掛掉。
>我直覺的往那個方向去想(忘了delete陣列,物件型態本來就不能轉型)。
>但如果不是這個原因,就不知道VC6那裡犯錯了。

VC6 1989年出版, C++ 標準在 1989 年設定, 它有 80% 符合標準就不錯了. 曾經有測試它只有 73% 符合標準而已.
  www.cs.clemson.edu/~malloy/projects/ddj/paper.pdf

除此外, 它的 STL 根本就是 broken 的, 而且還不是透過 service packs 來解決, 你必須根據 KB 文件手動的修改函式庫的原始碼.

用一個非常不符合標準的編譯器來測試語言標準的特性, 這不是開玩笑嗎? 
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/28 上午 10:30:16
>用一個非常不符合標準的編譯器來測試語言標準的特性, 這不是開玩笑嗎?
嗯,從來不把VC[6]當標準,只是人在江湖身不由己,不得不用它。
(太多的東西需用VC6維護)
好奇想知道 p= new CB[0]; delete[]p;VC6是那裡搞錯了,早上追踪了一下:
發現p= new CB[n]; delete[]p;//<--不論n是多少,VC6永遠呼叫p[0]裡vtable
的deleting destructor(註)。即使n==0時,還是這樣。但n==0那裡來p[0]?
這個錯和我最初想歪的理由是一樣的,歪打正著XD。
從你VC2010測到的,看來VC想改正錯誤,但改了還是錯的,只是不一樣的錯。
(去了一個挫塞的,換來一個閃尿的XD)
看來deleting destructor根本是一個錯誤設計,兩者得分開才行。

(註)VC的destructor和operator delete都是在deleting destructor,deleting會不會
呼叫operator delete是由傳入的旗標引數決定。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/28 上午 11:00:47
to:cxxlman:
>>CM *pcm = new CM;
>>CK *pck = pcm;
>>CL *pcl = pck;//<--這個有問題吧?
>>只不過 Smart pointer 是由最後放棄持有的那個負責 delete,
>> 如上例萬一放棄持有的是 pcl, 那不就出問題了嗎
>OK,我沒想CK *pck = pcm;這種情況。
我想有點被你誤導了...
陣列是不能被轉型使用的,即使是陣列的smart pointer。
你的程式不也想法阻止陣列轉型嗎?
我最初的邏輯,樓主的這一行:
~U_ptr (){ delete ip; }//希望這邊可以判別ip指向單一物件還是矩陣後決定要用delete ip 或 delete [] ip
實現成
~U_ptr (){
if CA::IsArray(ip)//<--這個ip是不能被轉型的,否則-|
 delete [] ip;//<--ip若被轉型,這裡會出問題<----|
else
 delete ip;
}
除非更改delete[]架構,不然以上的分析應是正確的。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/28 上午 11:23:40
回自己:
>我想有點被你誤導了...
>陣列是不能被轉型使用的,即使是陣列的smart pointer。
不是陣列問題,是多重繼承啦!
所以...我被自己誤導了 XD
過年覺睡太多了,昏頭昏腦的,還是趕快上班吧,免得變成一隻小豬,呵呵...
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/28 下午 04:02:01

>>用一個非常不符合標準的編譯器來測試語言標準的特性, 這不是開玩笑嗎?
>嗯,從來不把VC[6]當標準,只是人在江湖身不由己,不得不用它。
>(太多的東西需用VC6維護)
>好奇想知道 p= new CB[0]; delete[]p;VC6是那裡搞錯了,早上追踪了一下:
>發現p= new CB[n]; delete[]p;//<--不論n是多少,VC6永遠呼叫p[0]裡vtable
>的deleting destructor(註)。即使n==0時,還是這樣。但n==0那裡來p[0]?
>這個錯和我最初想歪的理由是一樣的,歪打正著XD。
>從你VC2010測到的,看來VC想改正錯誤,但改了還是錯的,只是不一樣的錯。
>(去了一個挫塞的,換來一個閃尿的XD)
>看來deleting destructor根本是一個錯誤設計,兩者得分開才行。
>
>(註)VC的destructor和operator delete都是在deleting destructor,deleting會不會
>呼叫operator delete是由傳入的旗標引數決定。

應該是 VC6 犯了一個失誤,應當先檢查陣列中是不是有元素,若有再去呼叫 deleting destructor ,否則只須呼叫 operator delete[] 這樣就對了
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/28 下午 04:35:35
>應該是 VC6 犯了一個失誤,應當先檢查陣列中是不是有元素,
>若有再去呼叫 deleting destructor ,否則只須呼叫 operator delete[] 這樣就對了
因為VC支援你之前提到的神奇功能,可以delete一個降轉的陣列指標。
VC怎麼做到的?就是delete陣列時的解構和呼叫operator delete工作都是呼叫
deleting destructor做的。雖然元素0的陣列不必解構,但為了能正確的呼叫
operator delete,所以還是取用了第一個物件的vtable,慘的是根本沒有第一個物件!
依R大測試VC2010的結果看,VC想修正這個錯誤,所以在發現陣列元素為0時,就不
進入deleting destructor找operator delete。而是直接用global的::operator delete。
雖然不當了,但還是錯的,真是服了Microsoft!
其實陣列若依++標準不能降轉,那麼要找正確的ooperator delete可以static lookup
就行了。因為VC還賴著delete一個降轉的陣列指標,因不確定可以static lookup。
所以甘脆就放棄static lookup,直接使用::operator delete。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/28 下午 08:12:52

>>應該是 VC6 犯了一個失誤,應當先檢查陣列中是不是有元素,
>>若有再去呼叫 deleting destructor ,否則只須呼叫 operator delete[] 這樣就對了
>因為VC支援你之前提到的神奇功能,可以delete一個降轉的陣列指標。
>VC怎麼做到的?就是delete陣列時的解構和呼叫operator delete工作都是呼叫
>deleting destructor做的。雖然元素0的陣列不必解構,但為了能正確的呼叫
>operator delete,所以還是取用了第一個物件的vtable,慘的是根本沒有第一個物件!
>依R大測試VC2010的結果看,VC想修正這個錯誤,所以在發現陣列元素為0時,就不
>進入deleting destructor找operator delete。而是直接用global的::operator delete。
>雖然不當了,但還是錯的,真是服了Microsoft!
>其實陣列若依++標準不能降轉,那麼要找正確的ooperator delete可以static lookup
>就行了。因為VC還賴著delete一個降轉的陣列指標,因不確定可以static lookup。
>所以甘脆就放棄static lookup,直接使用::operator delete。

我是覺得 deleting destructor 再配合 http://www.programmer-club.com.tw/ShowSameTitleN/c/44092.html 提出的 operator[] 正好可以解決物件陣列諸多問題, 也許 VC 有意這麼做, 不然實在看不出 VC 為 物件陣列 做 deleting destructor 的用意何在
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/28 下午 10:31:40
>我是覺得 deleting destructor 再配合 http://www.programmer-club.com.tw/ShowSameTitleN/c/44092.html
>提出的 operator[] 正好可以解決物件陣列諸多問題, 也許 VC 有意這麼做, 不然實在看不出 VC 為 物件陣列 做
>deleting destructor 的用意何在
VC 只是解決降轉陣列指標可以delete[]而已,至於陣列降轉產生的諸多問題根本沒有碰觸。
程式發生了非法的降轉陣列指標(已經拉肚子了),但在最後delete[]那個拉肚子的指標,VC可以正確的刪除而已。
說難聽一點的就是程式拉肚子,只要沒拉死而撐到delete[]這個位置,那麼VC承諾幫你擦屁屁。
(然後為了擦屁屁卻誤了正事)
你提出的operator[]從說明不足以表達功用,範例也諸多疑問(R大提出了一些)。
物件陣列轉型的問題並不單純,光一個合理的使用概念都還付之缺如,就不必為它傷腦筋吧。
但陣列物件(一個可當做陣列的物件),倒是很容易做到降轉的目的:
class AA{
public:
virtual int operator[](int idx);
};

class BB:public AA{
virtual int operator[](int idx);
};

void main()
{
AA &aa= * new BB;//BB降轉成AA
int iR=aa[3];//ok to call BB::operator[]()
delete [] &aa;
}
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/29 上午 06:58:44
一個可以當作陣列的物件就是容器的用法,也是目前 operator[] 的普遍用法,這種用法和陣列一點關係也沒有,你不覺得很可惜嗎?另一個可惜就是 reference,它根本和指標沒兩樣多此一舉,C++ 原本可以在這兩項多下點功夫,若把以下

virtual B& operator[](unsigned Index)
{
 return *(this+Index);
}

作為 reference 內建的功能,我覺得很完美,我也找不出會有什麼問題


作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/29 下午 04:59:55
>一個可以當作陣列的物件就是容器的用法,也是目前 operator[] 的普遍用法,
>這種用法和陣列一點關係也沒有,你不覺得很可惜嗎?另一個可惜就是 reference,
>它根本和指標沒兩樣多此一舉,C++ 原本可以在這兩項多下點功夫,若把以下
從底層看,不只reference是指標,int i;也是指標。電腦本來就是指標(位址)容器在運作。
其它都是組合和抽象化後的觀點(但都可以還原指標觀點)。
你要的功能本來就可以從陣列容器下手。
C++很多運算子重載,看起來很自由,但還是有一條紅線,就是C++的本質。
舉個例,你可以重載operator加減乘除,但你不能改變運算子的優先權。
所以一個加(減乘除)運算,不是一開始就交給你的operator+,而是紅線一邊C++
做,另一邊才交給重載的perator+。
同樣的operator new/delete也是一樣,一個new/delete運算並不是一開始就完全
交到可重載的operator new/delete手上,而是紅線一邊C++做,另一邊才交給
重載的perator new/delete。
再談陣列下標([])運算,也是一樣的道理,global下標的位移計算是屬於C++紅線這邊,
不能給程式員亂搞,程式員的權利就只能在類別範圍內。
這樣子出問題只會出在類別和物件上,不會導致C++語言崩潰。
沒有了紅線,C++就成了X++不再是C++了。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/29 下午 06:20:02

>>一個可以當作陣列的物件就是容器的用法,也是目前 operator[] 的普遍用法,
>>這種用法和陣列一點關係也沒有,你不覺得很可惜嗎?另一個可惜就是 reference,
>>它根本和指標沒兩樣多此一舉,C++ 原本可以在這兩項多下點功夫,若把以下
>從底層看,不只reference是指標,int i;也是指標。電腦本來就是指標(位址)容器在運作。
>其它都是組合和抽象化後的觀點(但都可以還原指標觀點)。
>你要的功能本來就可以從陣列容器下手。
>C++很多運算子重載,看起來很自由,但還是有一條紅線,就是C++的本質。
>舉個例,你可以重載operator加減乘除,但你不能改變運算子的優先權。
>所以一個加(減乘除)運算,不是一開始就交給你的operator+,而是紅線一邊C++
>做,另一邊才交給重載的perator+。
>同樣的operator new/delete也是一樣,一個new/delete運算並不是一開始就完全
>交到可重載的operator new/delete手上,而是紅線一邊C++做,另一邊才交給
>重載的perator new/delete。
>再談陣列下標([])運算,也是一樣的道理,global下標的位移計算是屬於C++紅線這邊,
>不能給程式員亂搞,程式員的權利就只能在類別範圍內。
>這樣子出問題只會出在類別和物件上,不會導致C++語言崩潰。
>沒有了紅線,C++就成了X++不再是C++了。

C++ 沒有陣列, 物件陣列是 C 的, 不是 C++ 的, 因 C++ 是 OO 的實作, 但物件陣列不淮(不是不能)轉型, 不能在父類別層級上做陣列操作, 所以物件陣列是 C 不是 C++

我無意要用 operator [] 實踐物件陣列, 因那得每個 class 都要實作, 不切實際, 我只是要指出 C++ 是可以有物件陣列的, 但已經錯過, 雖然此討論最後找到解決樓主須求的方法, 但我覺得是做白工了, 因 C++ 沒有物件陣列, 所以根本就用不到
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/29 下午 09:34:42
>C++ 沒有陣列, 物件陣列是 C 的, 不是 C++ 的,
這個聽來蠻奇怪的...C有的C++就不能有?

>因 C++ 是 OO 的實作, 但物件陣列不淮(不是不能)轉型,
>不能在父類別層級上做陣列操作, 所以物件陣列是 C 不是 C++
想想看陣列轉型要付的代價(至少RTTI就不再是個選項)

>我無意要用 operator [] 實踐物件陣列, 因那得每個 class 都要實作, 不切實際,
請用template
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/30 上午 08:10:29

>>C++ 沒有陣列, 物件陣列是 C 的, 不是 C++ 的,
>這個聽來蠻奇怪的...C有的C++就不能有?

只能擁有 C 部份, 沒有 C++ 的特色

>>因 C++ 是 OO 的實作, 但物件陣列不淮(不是不能)轉型,
>>不能在父類別層級上做陣列操作, 所以物件陣列是 C 不是 C++
>想想看陣列轉型要付的代價(至少RTTI就不再是個選項)

>>我無意要用 operator [] 實踐物件陣列, 因那得每個 class 都要實作, 不切實際,
>請用template

你是說有陣列物件, 不是物件陣列, 你如何保證如下能正確運作

void F(CA ca[])
{
....
}
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/30 下午 12:35:03
>你是說有陣列物件, 不是物件陣列, 你如何保證如下能正確運作
>void F(CA ca[])
ca[]是物件陣列,物件陣列的下標計算由編譯器計算,不能重載。
(就是我說的那條紅線!)
它可以正確運作,但不是你要呼叫CA的operator[]。
void F(CA ca)<--CA若是個陣列類別,那麼ca就是陣列物件,可以當陣列使用。
要讓語意更清楚就是在命名上下手:
void F(CArray ca)
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/30 下午 02:40:32

>>你是說有陣列物件, 不是物件陣列, 你如何保證如下能正確運作
>>void F(CA ca[])
>ca[]是物件陣列,物件陣列的下標計算由編譯器計算,不能重載。
>(就是我說的那條紅線!)
>它可以正確運作,但不是你要呼叫CA的operator[]。
>void F(CA ca)<--CA若是個陣列類別,那麼ca就是陣列物件,可以當陣列使用。
>要讓語意更清楚就是在命名上下手:
>void F(CArray ca)

這個 CArray 是什麼?

我的意思是 void F(CA ca[]) 在 C 是安全的, 但在 C++ 就不安全了, 以下 F(CB_Array); 不會被編譯器阻止, 因它是正確的, 只是會當掉


void F(CA ca[])
{
...
}

class CA
{
}

class CB:public CA
{
}

main()
{
  CA CA_Array[5];
  CB CB_Array[5];

  F(CA_Array);
  F(CB_Array);

}

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/30 下午 04:57:45
>void F(CA ca[]);
>:::::::::::::::::::::::
>main()
>{
>CB CB_Array[5];
>F(CB_Array);
>}

F(CB_Array);和 CA *pCA_Array= new CB_Array[5];是一樣的意思。
之前討論過了,物件陣列不能轉型使用,
所以VC支援delete一個轉型的物件陣列沒有意義。
編譯器不提出錯誤是因為C/C++把陣列和指標視為同型。
F(CA ca[]);函式宣告參數ca是一個物件陣列,物件陣列既不能轉型
,程式硬是轉型使用,要期待C++能怎樣?只能當給你看。
這些你應該都早已知道,所以也不知你想要的是什麼?
修改C++的標準?努力吧,有許有朝一日可以成為制定標準的委員。
想要例用C++編譯器不提出異議,而又要讓void F(CA ca[]);可以依
你期望的正確索引CB物件陣列?
這是不可能的,能做的大概只有二種:
1.使用陣列物件(前提到的CArray就是陣列物件的類別)
2.物件全採用指標,物件陣列其實是物件指標的陣列,那void F(CA ca[]);
 就可以降轉運作。(因為陣列元素的大小都一致了)

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/30 下午 05:11:25

>>void F(CA ca[]);
>>:::::::::::::::::::::::
>>main()
>>{
>>CB CB_Array[5];
>>F(CB_Array);
>>}
>
>F(CB_Array);和 CA *pCA_Array= new CB_Array[5];是一樣的意思。
>之前討論過了,物件陣列不能轉型使用,
>所以VC支援delete一個轉型的物件陣列沒有意義。
>編譯器不提出錯誤是因為C/C++把陣列和指標視為同型。
>F(CA ca[]);函式宣告參數ca是一個物件陣列,物件陣列既不能轉型
>,程式硬是轉型使用,要期待C++能怎樣?只能當給你看。
>這些你應該都早已知道,所以也不知你想要的是什麼?
>修改C++的標準?努力吧,有許有朝一日可以成為制定標準的委員。
>想要例用C++編譯器不提出異議,而又要讓void F(CA ca[]);可以依
>你期望的正確索引CB物件陣列?
>這是不可能的,能做的大概只有二種:
>1.使用陣列物件(前提到的CArray就是陣列物件的類別)
>2.物件全採用指標,物件陣列其實是物件指標的陣列,那void F(CA ca[]);
> 就可以降轉運作。(因為陣列元素的大小都一致了)
>

我只是要回答你 C++ 沒有陣列, 因 F(CA ca[]) 函數沒法在 C++ 使用, 我再用了一個例子回答你, 這樣回答清楚了吧

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/30 下午 05:40:43
>我只是要回答你 C++ 沒有陣列,
你說這個,十個大概有九個人會回你:C++怎麼會沒有陣列!?
算了,反正也搞不清你的意思,就這樣了。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/30 下午 09:19:32

>>我只是要回答你 C++ 沒有陣列,
>你說這個,十個大概有九個人會回你:C++怎麼會沒有陣列!?
>算了,反正也搞不清你的意思,就這樣了。

應該說是十個也找不到一個會去用物件陣列, 至於懂不懂看機緣囉
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/31 下午 12:13:10
我還是再講清楚比較好,是不是聽得懂就隨緣了,我用集合邏輯來說明

假設 C 的陣列條件為 a,C++ 比較嚴苛要 a+b 條件,如果有 C++ 陣列這種東西,那麼就可以畫一個大圈圈代表 "C 陣列的集合",裡面有一個小圈圈代表 "C++ 陣列的集合",但 C++ 並沒有準備好 a+b 條件,所以 "C++ 陣列的集合" 並不存在,連帶 C 陣列的集合中也沒有 "C++ 陣列的集合",C++ 在陣列的用法上是退化成 C 在用,所以用的是 "C 陣列的集合",且不含一丁點 "C++ 陣列的集合"

為什麼 C++ 不能用 C 的陣列就好,因 C 的陣列在 C++ 的環境下有不足之處,缺了 b 條件,若把 C++ 當作 C 來用,就像鴕鳥心態把問題視而不見,可是問題依然存在

我的能力就只能說到這了,要我再說具體一點還真力有不及見諒了


 板主 : simula
 > C++ - 討論區
 - 最近熱門問答精華集
 - 全部歷史問答精華集
 - C++ - 知識庫
  ■ 全站最新Post列表
  ■ 我的文章收藏
  ■ 我最愛的作者
  ■ 全站文章收藏排行榜
  ■ 全站最愛作者排行榜
  ■  月熱門主題
  ■  季熱門主題
  ■  熱門主題Top 20
  ■  本區Post排行榜
  ■  本區評價排行榜
  ■  全站專家名人榜
  ■  全站Post排行榜
  ■  全站評價排行榜
  ■  全站人氣排行榜
 請輸入關鍵字 
  開始搜尋
 
Top 10
評價排行
C++
1 Raymond 13050 
2 青衫 4760 
3 simula 4690 
4 coco 4030 
5 白老鼠(Gary) 3670 
6 ozzy 2540 
7 Ben 2250 
8 Anderson 1960 
9 windblown 1650 
10 Kenny 1560 
C++
  專家等級 評價  
  一代宗師 10000  
  曠世奇才 5000  
  頂尖高手 3000  
  卓越專家 1500  
  優秀好手 750  
Microsoft Internet Explorer 6.0. Screen 1024x768 pixel. High Color (16 bit).
2000-2019 程式設計俱樂部 http://www.programmer-club.com.tw/
0.546875