討論區快速選單
知識庫快速選單
傑米的攝影旅遊筆記 網路投保旅行平安險 政府補助!學嵌入式+物聯網
[ 回上頁 ] [ 討論區發言規則 ]
為什麼解構子不可以是純虛擬?
更改我的閱讀文章字型大小
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/7 下午 07:23:34
如題
今天不小心碰到,編譯不過,想不起原由了...
作者 : ozzy123(ozzy) VC++優秀好手資訊類作業求救卓越專家C++卓越專家貼文超過4000則人氣指數超過30000點
[ 貼文 4499 | 人氣 37262 | 評價 11100 | 評價/貼文 2.47 | 送出評價 49 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/7 下午 07:43:10
this is a logic problem
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/7 下午 09:43:36
純虛擬解構式,C++可以給一個預設解構函式。
解構函式若不適用,就需自寫解構函式.
這樣有什麼邏輯問題?
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/8 上午 01:44:48
>如題

沒這回事. C++ 12.4, p7
A destructor can be declared virtual or pure virtual; if any objects of that class or any derived class are created in the program, the destructor shall be defined. ...

>今天不小心碰到,編譯不過,想不起原由了...

是編譯不過還是 link 不過? 你有沒有把它的定義寫出來? 定義必須分開寫:
〔.h〕
  class C
  {
  public:
    virtual ~C(void) = 0;
  };

〔.cpp〕
  C::~C(void)
  {
  }

請貼出完整程式碼, 並註明編譯器版本.
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/8 上午 11:50:12
>  C::~C(void)
>  {
>  }
有了C::~C(void),那class C的解構子(~C()=0;)還能算是純虛擬嗎?
我是在VC6上做的,範例如下:
class T1
{
public:
virtual ~T1()=0;
};

class T2:public T1
{
int m_i;
public:
virtual ~T2();
};
T2::~T2()
{
m_i=0;
}

main()
{
T1 *p= new T2;
delete p;
//:::::::::::::::::::::::::::::::::::::::
}
編譯果是link unresolved symbol:
error LNK2001: unresolved external symbol "public: virtual __thiscall T1::~T1(void)"
我知道加上T1 destrutor定義當然可以過關:
T1::~T1()
{
}
但這樣T1的destructor還是算是"純"虛擬嗎?
無意中出現這個link error,想起以前有這個印象:解構子不可以是純虛擬。
雖然可以把解構函式宣告宣告成純虛擬,但實際使用該類別時,一定得定義。
這樣的話我認為那已不是純虛擬了,只是語法上允許你在解構子上加=0而已。
忘了為什麼,或許以前不求甚解,跟本從來不知道為什麼XD
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/8 下午 12:47:19

>>  C::~C(void)
>>  {
>>  }
>有了C::~C(void),那class C的解構子(~C()=0;)還能算是純虛擬嗎?

C++ 10.4 Abstract class
  2. An abstract class is a class that can be used only as a base class of some other class; no object of an abstract class can be created except as sub-objects of a class derived from it. A class is abstract if it has at least one pure virtual function. [Note: such a function can be inherited; see below.] A virtual function is specified pure by using a pure-specifier (9.2) in the function declaration in the class declaration. A pure virtual function need be defined only if explicitly called with the qualified-id syntax (5.1).

9.2 class members
  pure-specifier:
    = 0

也就是說, = 0 就構成 pure virtual 的條件 (同時也構成了 abstract class 的條件), 跟有無定義沒有關係.

事實上, 寫出 pure-virtual function 的定義也不少見, 和一般的 (非-pure) virtual function 一樣, derived 可以呼叫 base 的 virtual 成員 (比方說實施 default behavior) 在這情況下, 當然要有定義.


>想起以前有這個印象:解構子不可以是純虛擬。

沒這回事. 純虛擬函式寫定義, 及純虛擬解構子其實還滿常用的, GotW#31 有特別提到:
  http://www.gotw.ca/gotw/031.htm

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/8 下午 01:38:18
可能是 c++ 沒有純介面的觀念,在 class 內可能會有東西的情形下,Destructor 不能為純虛擬函數這種概括性規定也算合理,多加個 { } 也沒什麼副作用不是嗎
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/8 下午 02:18:40
>A pure virtual function need be defined only if explicitly called with the qualified-id syntax (5.1).
這句話我理解。
但一個純虛擬函式,又實作了它,那麼那個=0純虛擬的宣告還有意義嗎?
class A1{
virtual void fun()=0;
};
void A1::fun(){...}
和這樣寫有什麼不同:
class A2{
virtual void fun();
};
void A2::fun(){...}
我覺得第一個class A1的=0宣告已沒有"意義"了,我也理解在C++上兩個class的"作用"
上還是有所差異=>class A1不能拿來建立物件,class A2則可。
所以純虛擬=0的目的已不在該函式的"純",而是用成員函式的"純"來宣告class A1的抽象性質。
真蠢啊!
從語義的合理性來說,若是要宣告class A1 是抽象類別,語言設計上這樣會比較合理:
class abstract A1{
virtual void fun();
};
void A1::fun(){...}
而不是用一個非"純"抽象的成員硬冠上pure來表示class的抽象性。
從這個觀點,這篇文章:
http://www.gotw.ca/gotw/031.htm
所提的理由就通通不像是理由了。

OK,反正我也不是C++制定者或改革者,只是一個使用者而已,就從使用者的困擾來談吧。
C++既然把"純"虛擬定義得如此不純,我就承受吧。但有一個問題,純虛擬函式常用來定義
介面,例如:
//Interface.h
class CInterface{
virtual int fun1()=0;
virtual int fun2()=0;
virtual ~CInterface()=0;
};
這介面可能發給別人使用,實作CInterface類別和物件則在DLL。
問題來了,我除了給使用者Interface.h這個介面外,還要給一個定義
CInterface::~CInterface()的.c檔?怎麼看都怎麼怪....
突然想到,我以前寫過的介面類別是怎麼麼做的,去找找....
原來我是像這樣寫 XD
//Interface.h
class CInterface{
virtual int fun1()=0;
virtual int fun2()=0;
virtual ~CInterface(){};//<--直接在這裡加{},不用=0了
};
雖然比給一個怪怪的.c檔好些,但心裡還是很不舒坦:(
為什麼C++可以給預設解構式就不能給預設的純虛擬解構子呢?
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/8 下午 10:21:19
Destructor 不能為"純虛擬函數又未定義",用這例子說明

class AA
{
 int *pI; // 這要怎麼 delete
public:
 AA()
 {
  pI = new int;
 }

 virtual ~AA() = 0;
};

純虛擬函數是指必須有繼承者,並必須覆寫,純虛擬函數的定義是可以被繼承者呼叫用的,如下例子

class A
{
public:
 virtual void f() = 0;
/* 發現 gcc 4.6.1 不能在這定義
 {
  cout << "A::f" << endl;
 }
*/
};


void A::f()
{
 cout << "A::f" << endl;
}


class B:public A
{
public:
 virtual void f()
 {
  cout << "B::f" << endl;
  A::f();
 }
};

作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/9 上午 12:54:32

>>A pure virtual function need be defined only if explicitly called with the qualified-id syntax (5.1).
>這句話我理解。
>但一個純虛擬函式,又實作了它,那麼那個=0純虛擬的宣告還有意義嗎?

從純 OO 設計的角度來看, pure virtual function 應該只有宣告, 沒有定義, 因為它等待被覆寫.

但這是語法, 而語法允許它有定義.

若是語法也不允許它有定義, 你要實現 default behavior 就比較麻煩了, 你必須再寫一個 intermediate base, 覆寫純虛擬函式來做 default behavior, 然後才繼承自這個 intermediate base.

這個應該也有不少人投訴吧? 要做到每個人都滿意是不可能的.

也許你看不慣的只是「純虛擬」這幾個字, 認為它不符合現有語言的行為? 這個應該比較好解決.


>從語義的合理性來說,若是要宣告class A1 是抽象類別,語言設計上這樣會比較合理:
>class abstract A1{
>virtual void fun();
>};

=0 的目的除了宣告 class 的抽象性外, 它還有多一層意義: 如果 derived class 要 instantiate, 它必須要 override 所有 =0 的函式.

單新增 abstract 關鍵字只能表達 class 的抽象性, 但無法反映出需要 override 的函式. 在函式那裡還是需要有不同的語法來標出「必須覆寫」這個特性, 而這樣子的話, 'abstract' 又變得多餘了.

唯一需要 abstract 這個關鍵字地方是當 class 完全沒有「必須覆寫」的函式, 但你又不希望該 class 可以被 instantiate 的情況下.

但新增關鍵字是標準委員會在編定新標準時儘可能避免的, 因為所有用到 abstract 這個名字的程式都會忽然之間無法編譯; 除非這個新增的關鍵字做到了現有語言無法做到的, 標準委員會才會給予考慮.

新增 abstract 關鍵字並沒有帶來什麼新的東西.

我同意 =0 這個語法不是很好看, 也許用 =pure, pure virtaul 或 override 會更好的詮釋它的概念, 但基於同樣原因, 這些都不可能實現.

>但有一個問題,純虛擬函式常用來定義
>介面,例如:
>//Interface.h
>class CInterface{
>virtual int fun1()=0;
>virtual int fun2()=0;
>virtual ~CInterface()=0;
>};
>這介面可能發給別人使用,實作CInterface類別和物件則在DLL。
>問題來了,我除了給使用者Interface.h這個介面外,還要給一個定義
>CInterface::~CInterface()的.c檔?怎麼看都怎麼怪....

如果有至少一個純虛擬函式, 解構子虛擬就可以了, 根本就不需要純虛擬. 如上面所說, 純虛擬解構子只有在當你要設計一個完全沒有其它純虛擬函式的介面才需要.


>突然想到,我以前寫過的介面類別是怎麼麼做的,去找找....
>原來我是像這樣寫 XD
>//Interface.h
>class CInterface{
>virtual int fun1()=0;
>virtual int fun2()=0;
>virtual ~CInterface(){};//<--直接在這裡加{},不用=0了
>};
>雖然比給一個怪怪的.c檔好些,但心裡還是很不舒坦:(
>為什麼C++可以給預設解構式就不能給預設的純虛擬解構子呢?

以目前現有語法來說, 要在什麼情況下提供預設的虛擬解構子, 或純虛擬解構子? 在沒有新增 abstruct 關鍵字的情況下應該不太可能攬括所有的可能性. 以目前現有語法的能力也沒有這個必要.


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

>
>如果有至少一個純虛擬函式, 解構子虛擬就可以了, 根本就不需要純虛擬. 如上面所說, 純虛擬解構子只有在當你要設計一個完全沒有其它> 純虛擬函式的介面才需要.

由兩方向來看

純虛擬解構子(有 = 0的解構子) 加上 定義 是應該的,因 c++ 並沒有單純介面的概念

有定義的解構子加上 =0 , 這就奇怪了,若有一般的純虛擬函式存在這個 =0 就多餘了,若沒有一般的純虛擬函式存在,用 =0 強迫做繼承有什麼意義?

所以解構 =0 加在解構子上覺得很突兀,事實上沒這必要
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/9 上午 08:11:07
>由兩方向來看
>純虛擬解構子(有 = 0的解構子) 加上 定義 是應該的,因 c++ 並沒有單純介面的概念
>有定義的解構子加上 =0 , 這就奇怪了,若有一般的純虛擬函式存在這個 =0 就多餘了,

沒錯.


>若沒有一般的純虛擬函式存在,用 =0 強迫做繼承有什麼意義?

一般是不會有這個情況, 我唯一能想到的就是在設計的初期, 知道要用 abstract class, 但純虛擬的方法還沒確定,\ 就可以用純虛擬解構子來暫時確定 abstract class 的使用.


>所以解構 =0 加在解構子上覺得很突兀,事實上沒這必要

但至少語言提供了這個做法, 在程式設計上也滿足了不同的需求.
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/9 下午 12:29:08
討論有點混亂了...可能一開始我的提問用字就不精準導致。原提問應該一開始就要加入抽象字眼。
但因C++語言不用這個字,我避開了它,但反而語義不明。
>從純 OO 設計的角度來看, pure virtual function 應該只有宣告, 沒有定義, 因為它等待被覆寫.
>但這是語法, 而語法允許它有定義.
我對程式語言的語法定義並沒有太大的意見(反正人小言微,也影響不了什麼),但還是先澄清一些
上面的混亂。
pure virtual function,則是定義一個抽象的virtual function,抽象的本意應是沒有實作(定義)。
一個含有無實作定義的virtual function的類別,當然也成了抽象類別了。
問題在於C++又允許pure virtual functionvirtual function一樣可以實作,而類別也真的實作
了pure virtual function,這時候pure virtual function就已經失去了它的抽象性了(但virtual特性還在)。
失去了抽象性的pure virtual function,它的pure還有存在意義嗎?對function來說已經沒有了。
但C++允許它修飾類別為抽象類別,使得它又似乎有點用處。
這樣的語言設計,個人是覺得變得結構鬆散了些,舉個例子:
class A{
public:
virtual void vf1()=0;
virtual void vf2()=0;
virtual ~A()=0;
};
void A::vf1(){...}
void A::vf2(){...}
A::~A(){...}
這個例子,class A裡面所有的純虛擬函式全部都有實作。
宣告純虛擬的唯一目的就是宣告A是一個抽象類別。
但=0只需一個,其餘都是多餘的,要找那一個加=0,隨便!
多寫幾個=0,也沒關係,這是我說語言結構鬆散的理由。
OK,反正要作什麼可以達到目的就好了,這並不是我原發問的重點。

>以目前現有語法來說, 要在什麼情況下提供預設的虛擬解構子, 或純虛擬解構子?
若要設計一個"純"介面類別,例如一個傳輸介面:
class CInterface{
public:
virtual bool Read(...)=0;
virtual bool Write(...)=0;
virtual bool ~CInterface()=0;//我需要這個介面有一個抽象的解構子
};
CInterface的兩個實作:
class CInterfaceCOM:public CInterface{
public:
bool Read(...);
bool Write(...);
CInterfaceCOM();
~CInterfaceCOM();//實作virtual ~CInterface()
};
class CInterfaceUSB:public CInterface{
public:
bool Read(...);
bool Write(...);
CInterfaceUSB();
~CInterfaceUSB();//實作virtual ~CInterface()
};
問題的重點在於解構子無法抽象化(我不用"純虛擬",免得又搞混),
所以 virtual bool ~CInterface()=0;需要給一個實作:
CInterface::~CInterface(){...}
即使這個解構式實際上沒有做任何事,它的必需存在還是很奇怪。
這使得CInterface無法完全抽象而變得很尷尬。
如果有預設純虛擬解構子就可以把CInterface::~CInterface(){...}隱藏起來,
化解這一個尷尬。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/9 下午 06:29:14

>這個例子,class A裡面所有的純虛擬函式全部都有實作。
>宣告純虛擬的唯一目的就是宣告A是一個抽象類別。
>但=0只需一個,其餘都是多餘的,要找那一個加=0,隨便!
>多寫幾個=0,也沒關係,這是我說語言結構鬆散的理由。
>OK,反正要作什麼可以達到目的就好了,這並不是我原發問的重點。

以 C++ 的語言特性來說, 沒有介面的概念, 甚至連抽象類別的概念都沒有, 真的就只有虛擬函數, 你要哪個函數 "必須" 被覆寫一定要用 =0 標示清楚不能省, 至於純虛擬函數的那個定義, 不應把它看成內定實作, 因使用端永遠叫不到它, 它是給繼承者用的, 算是給繼承者的額外服務, 繼承者可叫用也可不叫用

>
>>以目前現有語法來說, 要在什麼情況下提供預設的虛擬解構子, 或純虛擬解構子?
>若要設計一個'純'介面類別,例如一個傳輸介面:
>class CInterface{
>public:
>virtual bool Read(...)=0;
>virtual bool Write(...)=0;
>virtual bool ~CInterface()=0;//我需要這個介面有一個抽象的解構子
>};
>CInterface的兩個實作:
>class CInterfaceCOM:public CInterface{
>public:
>bool Read(...);
>bool Write(...);
>CInterfaceCOM();
>~CInterfaceCOM();//實作virtual ~CInterface()
>};
>class CInterfaceUSB:public CInterface{
>public:
>bool Read(...);
>bool Write(...);
>CInterfaceUSB();
>~CInterfaceUSB();//實作virtual ~CInterface()
>};
>問題的重點在於解構子無法抽象化(我不用'純虛擬',免得又搞混),
>所以 virtual bool ~CInterface()=0;需要給一個實作:
>CInterface::~CInterface(){...}
>即使這個解構式實際上沒有做任何事,它的必需存在還是很奇怪。
>這使得CInterface無法完全抽象而變得很尷尬。
>如果有預設純虛擬解構子就可以把CInterface::~CInterface(){...}隱藏起來,
>化解這一個尷尬。

解構子的角色是為每一層 class 做善後工作, 須不須要應由各層 class 自行決定, 你為什麼須要用 =0 強制繼承者要提供呢?
另外 virtual 解構子的定義是一定會被叫用的, 不管有沒有加 =0
作者 : terenas(風) 貼文超過200則
[ 貼文 490 | 人氣 7440 | 評價 680 | 評價/貼文 1.39 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/9 下午 10:11:00

>純虛擬解構式,C++可以給一個預設解構函式。
>解構函式若不適用,就需自寫解構函式.
>這樣有什麼邏輯問題?
前題"你不能動base class", 假設:
某甲寫了一個base class 給你來繼承, 但他沒寫destructor, 而給了一個virtual destructor 給你, 由你來搞定. 若果你今天沒有source.
請問, 你要怎麼搞定好呢?
就算有source , 你還是得花時間去讀懂base class 才會知道, 要free 些什麼鬼.
再來, 若在private 中的呢? 怎麼辨?
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/9 下午 11:10:47
>以 C++ 的語言特性來說, 沒有介面的概念, 甚至連抽象類別的概念都沒有,
>真的就只有虛擬函數, 你要哪個函數 "必須" 被覆寫一定要用 =0 標示清楚不能省,
怎麼會連抽象類別都沒有?C++是根據OOP而來,沒有抽象類別不知道還能叫OOP
語言?只是C++語言裡沒有abstract這個關鍵字而已。
介面是一個概念,不管用什麼語言甚至合語言,你也可以實作介面功能。
你只能說某個語言對介面功能的支援好不好,不能說某個語言沒有介面的概念,
就不能實作介面。
我寫的那個傳輸介面CInterface的例子就是一個C++語言的實作介面例子。
它用了C++純虛擬的特性,直接且簡單的實現介面功能。所以也不能說
C++對介面的支援不佳,所差的就是我說的尷尬那一步,這也是我的疑問,
C++為什麼要差這一步?
另外一種比較複雜使用C++實現介面功能的範例,就是類似微軟COM C++實作。
它是用巢狀類別實現的,那個比較高級,可以當做異質間的介面。但也可以退化
到C++之間的介面(但還是比我那個例子複雜),完全沒有我所謂尷尬的問題。
除非情況需要,當然我也不會為了那一點尷尬而大動干戈。只是不解C++為什麼沒有
預設抽象解構子。

>至於純虛擬函數的那個定義, 不應把它看成內定實作, 因使用端永遠叫不到它,
>它是給繼承者用的, 算是給繼承者的額外服務, 繼承者可叫用也可不叫用
除了那個"純"另對類別有一點作用外,純虛擬函數給定義就成了虛擬函數一樣。
這點應該不用討論吧。

>解構子的角色是為每一層 class 做善後工作, 須不須要應由各層 class 自行決定,
>你為什麼須要用 =0 強制繼承者要提供呢?
我們談的是介面,不是class繼承問題。
介面沒有要繼承者提供解構式,甚至不是要給Client(使用端)繼承的基類別,所以根本不需有解構式。
(連建構子也沒有)
前面的例子,一個CInterfaceCOM或CInterfaceUSB的物件,給出CInterface介面(物件)
讓Client去操作虛擬功能Read和Write。
真正物件的建構和解構在CInterfaceCOM或CInterfaceUSB,都不在Client,就這麼簡單。

>另外 virtual 解構子的定義是一定會被叫用的, 不管有沒有加 =0
是的,這也是我一直說CInterface::~CInterface(){}這個實作很尷尬的原因。
CInterface作為一個介面跟本不要有解構式!(再次註明,介面不建構解構物件的)
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/9 下午 11:59:16

>>以 C++ 的語言特性來說, 沒有介面的概念, 甚至連抽象類別的概念都沒有,
>>真的就只有虛擬函數, 你要哪個函數 '必須' 被覆寫一定要用 =0 標示清楚不能省,
>怎麼會連抽象類別都沒有?C++是根據OOP而來,沒有抽象類別不知道還能叫OOP
>語言?只是C++語言裡沒有abstract這個關鍵字而已。
>介面是一個概念,不管用什麼語言甚至合語言,你也可以實作介面功能。
>你只能說某個語言對介面功能的支援好不好,不能說某個語言沒有介面的概念,
>就不能實作介面。
>我寫的那個傳輸介面CInterface的例子就是一個C++語言的實作介面例子。
>它用了C++純虛擬的特性,直接且簡單的實現介面功能。所以也不能說
>C++對介面的支援不佳,所差的就是我說的尷尬那一步,這也是我的疑問,
>C++為什麼要差這一步?
>另外一種比較複雜使用C++實現介面功能的範例,就是類似微軟COM C++實作。
>它是用巢狀類別實現的,那個比較高級,可以當做異質間的介面。但也可以退化
>到C++之間的介面(但還是比我那個例子複雜),完全沒有我所謂尷尬的問題。
>除非情況需要,當然我也不會為了那一點尷尬而大動干戈。只是不解C++為什麼沒有
>預設抽象解構子。

c++ 是比較精簡, 較底層的語言, 它的出發點比較像是提供零件的工具, 它只想提供能建構 oop 的零件就夠了, 並無意完全建構出整個 oo 系統, 這和高階語言的出發點有所不同

>>至於純虛擬函數的那個定義, 不應把它看成內定實作, 因使用端永遠叫不到它,
>>它是給繼承者用的, 算是給繼承者的額外服務, 繼承者可叫用也可不叫用
>除了那個'純'另對類別有一點作用外,純虛擬函數給定義就成了虛擬函數一樣。
>這點應該不用討論吧。
>
>>解構子的角色是為每一層 class 做善後工作, 須不須要應由各層 class 自行決定,
>>你為什麼須要用 =0 強制繼承者要提供呢?
>我們談的是介面,不是class繼承問題。
>介面沒有要繼承者提供解構式,甚至不是要給Client(使用端)繼承的基類別,所以根本不需有解構式。
>(連建構子也沒有)
>前面的例子,一個CInterfaceCOM或CInterfaceUSB的物件,給出CInterface介面(物件)
>讓Client去操作虛擬功能Read和Write。
>真正物件的建構和解構在CInterfaceCOM或CInterfaceUSB,都不在Client,就這麼簡單。
>
>>另外 virtual 解構子的定義是一定會被叫用的, 不管有沒有加 =0
>是的,這也是我一直說CInterface::~CInterface(){}這個實作很尷尬的原因。
>CInterface作為一個介面跟本不要有解構式!(再次註明,介面不建構解構物件的)

我在想你大概搞錯了 c++ 解構子的行為作用, c++ 解構子是一種由下往上(由子類往父類)串連執行的一種機制, 和一般虛擬函數不同的事, 虛擬解構子只是確保能帶到子類再往上執行, 用例子說明

假設 B 繼承自 A, C 繼承自 B,
B 如果不是 virtual, 那麼 delete B, 的執行順序 B --> A
B 如果是 virtual, 那麼 delete B, 的執行順序 C --> B --> A

這是內定機制不用程式員寫程式呼叫, 所以不能用一般虛擬函數去看待, 所以也沒有加上 =0 就要子類覆寫這種事


作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/10 上午 02:43:48
>討論有點混亂了...
>可能一開始我的提問用字就不精準導致。原提問應該一開始就要加入抽象字眼。
>但因C++語言不用這個字,我避開了它,但反而語義不明。

所以我認為這是 C++ 語言把 "pure virtual" 用在函式, 及 abstract 用在 class 的原因.


>>從純 OO 設計的角度來看, pure virtual function 應該只有宣告, 沒有定義, 因為它等待被覆寫.

我想對我之前這句話做個修訂及說明

最早的 OO 語言是 SIMULA, 在 SIMULA 67 Common Base Language(1970) 裡, 提到了 class, subclass, object, virtual 等概念, 但沒有 abstract, pure 等字眼. "Abstraction" 用在後來許多 OO 的概念裡, 但 "pure virtual" 應該是 C++ 語言才有的. 所以我上面這句話不太正確.

在 C++ 語言裡, pure virtual function 的意思是: 它可以不必定義, 但 non-abstract derived 必須要定義. 而不是說 base 不能有定義.

Pure virtual function 所造成的是抽象的 class, 而無法實現 (instantiate) 的是該 class 的物件.

如果你要嚴格的遵守 OOP 中 interface 的概念, 你可以不給定義 pure virtual function. 但能夠有定義實際上是對程式員提供了一些方便: 比方說, 一些共同的功能可以放在 pure virtual function 的定義裡, 減少複製的程式碼; 或是提供了 fall back to default 的功能.


>class A{
>public:
>virtual void vf1()=0;
>virtual void vf2()=0;
>virtual ~A()=0;
>};
>void A::vf1(){...}
>void A::vf2(){...}
>A::~A(){...}
>這個例子,class A裡面所有的純虛擬函式全部都有實作。
>宣告純虛擬的唯一目的就是宣告A是一個抽象類別。
>但=0只需一個,其餘都是多餘的,要找那一個加=0,隨便!

不對.

宣告純虛擬的目的是告訴非抽象的 derived: 你必須覆寫這些函式.
所以, 只有必需要 derived 覆寫的才要加 =0, 不是要你隨便亂加的.

>多寫幾個=0,也沒關係,這是我說語言結構鬆散的理由。

所以這個結論也不對了.


>>以目前現有語法來說, 要在什麼情況下提供預設的虛擬解構子, 或純虛擬解構子?
>若要設計一個"純"介面類別,例如一個傳輸介面:
>class CInterface{
>public:
>virtual bool Read(...)=0;
>virtual bool Write(...)=0;
>virtual bool ~CInterface()=0;//我需要這個介面有一個抽象的解構子

〔解構子前的 bool 應該是筆誤〕

>};

因為這個 class 已有其它的 pure virtual 函式, 所以解構子不需 pure virtual, virtual 就可以了 (你這個解構子的 =0 就真的是亂加).

你要的應該是: 編譯器會自動產生的解構子會根據 class 是否 abstract 來調適. Abstract class 會自動產生 virtual destructor, 非-abstract class 則產生普通的解構子.

很可惜, 語言標準並不允許這個. 在 base class 所有自動產生的解構子一律是 non-virtual 的. 如果要 virtual destructor, 你必須要宣告, 同時也要有定義.

但如果 base class 的解構子已是 virtual, 所有 derived class 自動產生的解構子也會是 virtual.

新的 C++ 標準 (C++11) 提供了一個新的語法: explicit-defaulted function. 也許會比較方便:
  class C {
  public:
    virtual ~C() = default;
  };

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

>>class CInterface{
>>public:
>>virtual bool Read(...)=0;
>>virtual bool Write(...)=0;
>>virtual bool ~CInterface()=0;//我需要這個介面有一個抽象的解構子
>>};
>>CInterface的兩個實作:
>>class CInterfaceCOM:public CInterface{
>>public:
>>bool Read(...);
>>bool Write(...);
>>CInterfaceCOM();
>>~CInterfaceCOM();//實作virtual ~CInterface()
>>};
>>class CInterfaceUSB:public CInterface{
>>public:
>>bool Read(...);
>>bool Write(...);
>>CInterfaceUSB();
>>~CInterfaceUSB();//實作virtual ~CInterface()
>>};
>>問題的重點在於解構子無法抽象化(我不用'純虛擬',免得又搞混),
>>所以 virtual bool ~CInterface()=0;需要給一個實作:
>>CInterface::~CInterface(){...}
>>即使這個解構式實際上沒有做任何事,它的必需存在還是很奇怪。
>>這使得CInterface無法完全抽象而變得很尷尬。
>>如果有預設純虛擬解構子就可以把CInterface::~CInterface(){...}隱藏起來,
>>化解這一個尷尬。


我還是看不出你的須求是什麼, 若是我只須把 CInterface 的解構子宣告成 virtual, 繼承者的解構子寫各自須要的就好, 保證一定會被叫到, 把解構子抽象化要做的事還不是一樣, 你那麼強調要把 CInterface 搞得像 java 的介面一樣, 除了比較明確好看外, 骨子裡做的還不是一樣

如果你是想要完全取代, 不要有往上層呼叫的情況, c++ 的解構子是做不到的, 若你要的是這個可能採用策略模式(Strategy)會比較適當吧
作者 : daniel(冷眼)討論區板主 VC++優秀好手遊戲程式設計優秀好手DirectX優秀好手C++優秀好手貼文超過1000則人氣指數超過70000點
[ 貼文 1564 | 人氣 84169 | 評價 6990 | 評價/貼文 4.47 | 送出評價 15 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/10 下午 12:36:31
解構函數.理論上本來就存在
你這樣的做法.應該是為了強制子物件繼承需定義解構
把這物件當成一個interface

不過大部人的做法
應該是自己在寫一個初始化/釋放函數
virtual bool inIt(void) = 0;
virtual bool release(void) = 0;


作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/10 下午 12:41:26
to: cxxlman
>我在想你大概搞錯了 c++ 解構子的行為作用, c++ 解構子是一種由下往上(由子類往父類)
>串連執行的一種機制, 和一般虛擬函數不同的事,
說過了,這個我懂。但請不要告訴我C++不能做Interface。

>我還是看不出你的須求是什麼, 若是我只須把 CInterface 的解構子宣告成 virtual,
>繼承者的解構子寫各自須要的就好, 保證一定會被叫到, 把解構子抽象化要做的事
>還不是一樣, 你那麼強調要把 CInterface 搞得像 java 的介面一樣, 除了比較明確好看外,
>骨子裡做的還不是一樣
>如果你是想要完全取代, 不要有往上層呼叫的情況, c++ 的解構子是做不到的, 若你要的
>是這個可能採用策略模式(Strategy)會比較適當吧
如果你曉得我說的Interface是什麼,就知道我要的什麼。
我用之前CInterface的例子套一個更實際的例子吧:
甲公司做了InterfaceUSB.dll,InterfaceCOM.dll...等通訊的dll。
甲公司的客戶程式要使用這些dll做為通訊,除了要給客戶所需的dll之外,還要給客戶
這些dll的共通介面,就是
//Interface.h
struct CInterface{
virtual bool Read(...)=0;
virtual bool Write(...)=0;
};
然後再加上一個export 函式:
CInterface * GetInterface();
對客戶端來說,它當然要include Interface.h
然後依需要load InterfaceUSB.dll或其它通訊介面的dll。
CInterface *pInterface= GetInterface();//得到通訊物件
之後客戶端程式就可以使用pInterface.Read(...)或pInterface.Write(...)
完全可不管pInterface的實質硬體是USB或COM或其它而達到虛擬裝置的目的。
所以CInterface不是給客戶當基類別繼承成為另一個類別,它就是一個介面規格,
僅此而已,介面物件也不是客端創建的,當然客戶端也不需解構它。
重點來了,一個提供這樣介面的USB dll應該怎麼寫?
簡便的做法:
class CInterfaceUSB:public CInterface{
public:
virtual bool Read(...);
virtual bool Write(...);
virtual CInterfaceUSB();
virutal ~CInterfaceUSB();//<--注意到這個解構子
};
CInterfaceUSB UsbObj;
extern "C" _declspec(dllexport) CInterface * GetInterface()
{return &UsbObj;}

大致上OK,但有一個問題出在CInterfaceUSB的解構子是虛擬的,但基類別沒有解構式
(使用預設非虛擬解構),這樣是有問題的,所以 CInterface會需要一個純虛擬的解構子
(是對解構子"純"抽象化):
struct CInterface{
::::::::::::::::::::::::::::::::::::::
pure virtual ~CInterface()=0;
};
因為CInterface是個介面,不應有解構實的定義,但C++在dll解構UsbObj時會
喚起CInterface::~CInterface(),所以又使得本來該是抽象的CInterface::~CInterface()
又得實作一個定義給它,雖然可以以它什麼事都不做的解構式,但還是有礙觀瞻。
(介面看起來不像介面)
最後再回到原問題的重點:
C++為什麼不提供預設虛擬解構式?
這樣上面的應用就不必有~CInterface()的定義來有礙觀瞻了。
當然骨子裡CInterfaceUSB的物件解構時,還是會呼叫~CInterface(),
但它不影響功能,表面也看不到(隱藏了),整個程式看起來合理就行了。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/10 下午 01:31:29
>宣告純虛擬的目的是告訴非抽象的 derived: 你必須覆寫這些函式.
>所以, 只有必需要 derived 覆寫的才要加 =0, 不是要你隨便亂加的.
>>多寫幾個=0,也沒關係,這是我說語言結構鬆散的理由。
>所以這個結論也不對了.
所以我才說C++語言結構鬆散啊 (這是一個可以鬆散胡寫的例子)XD

作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/10 下午 02:08:02
以下看看合不合乎你的須求, 其他的依樣畫葫蘆


Interface.h
-------------------
class ICInterface
{
public:
 ICInterface(){}
 virtual ~ICInterface(){}

 virtual bool Read(...)=0;
 virtual bool Write(...)=0; 
};

ICInterface *GetInterface();



InterfaceUSB_proxy.cpp
------------------------------------------
#include <InterfaceUSB.hpp> 假設有這個
#include "Interface.h"

class CCInterface:public ICInterface
{
 InterfaceUSB *proxy; 
public:
 CCInterface()
 {
  proxy = 依 InterfaceUSB 提供的方法建立
 }
 
 ~CCInterface()
 {
  ...做一些 InterfaceUSB 結束前須要的方法
  delete proxy;
 }

 bool Read(...)
 {
  proxy->InterfaceUSB 提供的方法
 }

 bool Write(...)
 {
  proxy->InterfaceUSB 提供的方法
 }
};

ICInterface * GetInterface()
{
 return new CCInterface;
}

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/10 下午 02:36:19
>解構函數.理論上本來就存在
>你這樣的做法.應該是為了強制子物件繼承需定義解構
>把這物件當成一個interface
反過來說,我是要實現一個interface的物件。
這樣吧,我參考COM interface的做法,改寫前面舉的
CInterface例子,可能大家比較會知道我的重點,也不會一直往繼承,解構順序上討論。
(有點複雜,我儘量簡化,但希望不要因此引發更多枝微末節爭議)
//Interface.h
struct CInterface{
virtual bool Read(...)=0;
virtual bool Write(...)=0;
//<--注意到interface就是interface,沒有建構,也沒解構
};
一個帶有 CInterface的類別CInterfaceUSB:
class CInterfaceUSB
{
::::::::::::::::::::::::::::::::::::
public:
 class CUsb : public CInterface//用巢狀類別實作Interface
 {
  virtual bool Read(...);
  virtual bool Write(...);
  //<--注意喔,還是沒有解構式,interface本來就沒有解不解構的問題
 }m_Usb;
friend class CUsb ;//使得介面實作可以存取CInterfaceUSB成員
CInterfaceUSB();//通訊類別有建構式
virtual ~CInterfaceUSB();//也有解構式
};
#define INTERFACE_PROLOGUE(theClass, localClass) \
theClass* pthis = \
((theClass*)((BYTE*)this - offsetof(theClass, m_##localClass)));
bool CInterfaceUSB::CUsb::Read(...)
{
INTERFACE_PROLOGUE(CInterfaceUSB,Usb ) //獲得指向CInterfaceUSB的指標 pthis以便操作物件
pthis->.......//可以用pthis操作CInterfaceUSB的成員了
}
bool CInterfaceUSB::CUsb::Write(...)
{請參考CInterfaceUSB::CUsb::Read}
CInterfaceUSB UsbObj;//創建USB通訊物件
extern "C" _declspec(dllexport) CInterface * GetInterface()
{return &UsbObj.m_Usb;}

GetInterface()得到的就是標準的介面,介面的操作就是依CInterface的規格。
CInterface只是介面規格(操作出入口),不是給客戶的類別繼承或創建物件的。
這樣的介面有沒有題,沒有!
它完全合乎介面的要求!(不要再說C++不能實作介面了)
唯一令人不滿的是,有點給它複雜,從interface轉回真正的類別物件(pthis)會有一點花費。
最主要的缺點是太複雜,如果一個project需要很多類似的介面,又不需要太嚴格,真的是麻煩。
所以才想要用實作類別繼承Interface類別的方式來實現Interface。
這樣做,真的簡單很多(雖然在保護上有不週的地方,但要簡單總得犧牲一些)
實際上也可行,但就是因為解構子不能"純"抽象化而有原問題的提出...
這樣問題夠清楚了吧?
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/10 下午 03:08:52
不是有條鐵律 "多用組合,少用繼承",你自己都知道你的問題用繼承沒法解決,還一直往裡頭鑽,我上面寫的範例,是一般解決統合不同元件的解決辦法,你仔細看看,有哪些地方沒法解決再提出來
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/10 下午 03:32:02
你先說明一下 CInterface, CInterfaceUSB, CUSB 這三者哪個是既成的, 哪個是你須要編寫的, 搞不清你的依存關係
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/10 下午 03:48:58
>不是有條鐵律 "多用組合,少用繼承",你自己都知道你的問題用繼承沒法解決,還一直往裡頭鑽,
本來就不是繼承問題,我也一直強調不是繼承問題。我不曉得是誰要一直往繼承裡鑽,拉都拉不住 :(

>你先說明一下 CInterface, CInterfaceUSB, CUSB 這三者哪個是既成的,
>哪個是你須要編寫的, 搞不清你的依存關係
那是我信手拈來的範例,三個都是我寫的,依存關係不重要。
重要的是CInterface, CInterfaceUSB要export同一套介面給客戶用。
再強調一次好了,介面是給客戶操作,不是給客戶繼承(真希望可以放大)。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/10 下午 04:20:59
>我上面寫的範例,是一般解決統合不同元件的解決辦法,你仔細看看,有哪些地方沒法解決再提出來
我沒有"沒法解決"的地方啊!我只是問"為什麼解構子不可以是純虛擬"
(純虛擬是對解構子而言是"純"抽象無實作之意,名詞可能用得不很恰當,為免再次誤會核先敘明)

>class ICInterface
>{
>public:
> ICInterface(){}
> virtual ~ICInterface(){}
> virtual bool Read(...)=0;
> virtual bool Write(...)=0; 
>};

你的這個例子,和我的有什麼不同?
Interface 不應該有任何有實體的成員。
virtual ~ICInterface(){}已經很尷尬了,
還多了一個ICInterface(){}?負負得正?
實際上用起來不會錯,我知道。
只是介面看起來不像介面。
沒什麼不能解決的問題,
一是湊合著用,二是用像前面我寫的COM Interface可以做到
完全Interface的要求。
我只是問為什麼C++不能給個預設虛擬建構式。
tahat all!
造成這麼多的誤會,浪費大家的時間,實在過意不去...
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/10 下午 04:49:57

>>我上面寫的範例,是一般解決統合不同元件的解決辦法,你仔細看看,有哪些地方沒法解決再提出來
>我沒有'沒法解決'的地方啊!我只是問'為什麼解構子不可以是純虛擬'
>(純虛擬是對解構子而言是'純'抽象無實作之意,名詞可能用得不很恰當,為免再次誤會核先敘明)
>
>>class ICInterface
>>{
>>public:
>> ICInterface(){}
>> virtual ~ICInterface(){}
>> virtual bool Read(...)=0;
>> virtual bool Write(...)=0; 
>>};
>
>你的這個例子,和我的有什麼不同?
>Interface 不應該有任何有實體的成員。
>virtual ~ICInterface(){}已經很尷尬了,
>還多了一個ICInterface(){}?負負得正?
>實際上用起來不會錯,我知道。
>只是介面看起來不像介面。
>沒什麼不能解決的問題,
>一是湊合著用,二是用像前面我寫的COM Interface可以做到
>完全Interface的要求。
>我只是問為什麼C++不能給個預設虛擬建構式。
>tahat all!
>造成這麼多的誤會,浪費大家的時間,實在過意不去...
COM Interface 用的 MS 的編譯器, 兩者都是他們自己製訂的, 愛怎樣就怎樣
標準 C++ 的介面都是那樣寫, C++ 並不是只為 oo 設計, 要力求最大公因最小公倍上的須求
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/10 下午 05:30:19
>COM Interface 用的 MS 的編譯器, 兩者都是他們自己製訂的,
我那個例子是100%用C++標準的語言寫的。
提COM的原因是,我承認是參考COM的技術,不是我從0開始,自行搞出來的。
作者 : daniel(冷眼)討論區板主 VC++優秀好手遊戲程式設計優秀好手DirectX優秀好手C++優秀好手貼文超過1000則人氣指數超過70000點
[ 貼文 1564 | 人氣 84169 | 評價 6990 | 評價/貼文 4.47 | 送出評價 15 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/10 下午 05:42:11
class ClassC
{
public:
ClassC()
{
AfxMessageBox("C" );
}
virtual ~ClassC()
{
AfxMessageBox("~C" );
}
};

class ClassB : public ClassC
{
public:
ClassB()
{
AfxMessageBox("B" );
}
virtual ~ClassB()
{
AfxMessageBox("~B" );
}
};

class ClassA : public ClassB
{
public:
};

ClassA a;

你用這測試吧~
應該就可以了解為什麼了吧
作者 : sflam(Raymond)討論區板主 Visual C++ .NET卓越專家VC++一代宗師新手入門優秀好手資訊類作業求救頂尖高手C++一代宗師貼文超過4000則
[ 貼文 4945 | 人氣 9172 | 評價 32290 | 評價/貼文 6.53 | 送出評價 142 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/10 下午 10:06:40
>我只是問為什麼C++不能給個預設虛擬建構式。

因為:
- POD,
- 不是所有的 class 都會拿來做繼承用,
- virtual 會增加成本; C++ 的哲學是: not making programmers pay for things that is not explicitly state.

要知道, C++ 不是個「純」OO 語言, 它其中一個設計理念是支援不同的 programming styles (procedural, data abstraction, object-oriented, generic)


C++11 新增了 explicitly-defaulted special member functions, 可以這麼寫:
  class ICInterface
  {
  public:
    ICInterface() = default;
    virtual ~ICInterface() = default;
    ...
  };

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/10 下午 10:11:47
>你用這測試吧~
>應該就可以了解為什麼了吧
了解什麼?又是建解構順序?
可不可以不要再教我建解構順序了orz
我的問題就只是為什麼C++不給預設的純虛擬解構式。
作者 : terenas(風) 貼文超過200則
[ 貼文 490 | 人氣 7440 | 評價 680 | 評價/貼文 1.39 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/10 下午 10:44:57

>>你用這測試吧~
>>應該就可以了解為什麼了吧
>了解什麼?又是建解構順序?
>可不可以不要再教我建解構順序了orz
>我的問題就只是為什麼C++不給預設的純虛擬解構式。
因為給預設的純虛擬解構式的意思等於 "你拉的屎, 由你的子孫來擦"
作者 : daniel(冷眼)討論區板主 VC++優秀好手遊戲程式設計優秀好手DirectX優秀好手C++優秀好手貼文超過1000則人氣指數超過70000點
[ 貼文 1564 | 人氣 84169 | 評價 6990 | 評價/貼文 4.47 | 送出評價 15 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/10 下午 11:08:18
了解你的不足

OBJECT-C 的解構元。跟c++的解構元有什麼不同呢
- ( void ) dealloc
{
[ super dealloc ];
}
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/10 下午 11:39:06
class CUsb:public CInterface, public coco // 加上這個
{
 virtual bool Read(...);
 virtual bool Write(...);
 //<--注意喔,還是沒有解構式,interface本來就沒有解不解構的問題
}m_Usb;


coco 的設計很簡單, 只要這樣

class coco
{
public
 virtual ~coco(){}
};

看看這樣能不能把 CUsb 內定解構子的 virtual 打開
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/11 上午 12:44:14
>因為:
>- POD,
>::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
終於...有善心人士針對我的問題回答了^^
既然用了virtual function已經表明,不是POD了(就看C++11放寬了POD範圍)

>- 不是所有的 class 都會拿來做繼承用,
是啊,做為interface的類別確實不是給繼承的。
所以不應該有建構解構式。
看我那個類COM的Interface範例,那應該是一種較正規的Interface做法。
但就是寫來有點沉重,如果我只是要到隔壁街買個東西,希望可以不必騎重型機車。
有個腳踏車踩一踩,還比較方便。
所以在實作介面時,把介面繼承不失為簡便的方法,但這樣就延生了介面類別平白跑出來
虛擬解構子這個尷尬的東西出來。就好像雖然找到腳踏車騎了,但腳踏後頭有個牌子寫著:
我不是腳踏車。很是尷尬。
預設純虛擬建構子是一個雖不滿意但可接受的方法,可惜C++沒有:(

>- virtual 會增加成本; C++ 的哲學是:
>not programmers pay for things that is not explicitly state
這一點算是有點理由。
但現在的最佳化技術應該可以克服。
其實提出這個問題,本來只是發發牢騷,但這兩天我還是有對自己的牢騷想了一些。
我"猜"最主要的癥結在於:
class A{
virtual ~A()=0;
};
=0的宣告並不表示,一定沒有解構式定義,所以在編譯時期並不能自動產生預設解構式。
到了連結時期發現有人換起A::~A()解構式時,已經來不及產生預設解構式了。
但這還是在技術上還是可以克服的,連結器並不是那麼純的只能做連結工作而已,
需要時它也可以產生預設程式碼,例如當連結器發現程式碼中沒有startup時,
就會自動產生startup。因為預設解構子很簡單,也沒有差異性,比startup更簡單。
要我再替C++找理由,就是找不到A::~A()解構式,可能是程式員該寫而忘了寫吧!
這時候自動產生預設解構式,可能反而隱藏一個bug。
(本來,還想自己反駁這個理由,但,一直自說自話,還是算了)

>C++11 新增了 explicitly-defaulted special member functions, 可以這麼寫:
>  class ICInterface
>  {
>  public:
>    ICInterface() = default;
>    virtual ~ICInterface() = default;
>   ...
>  };
virtual ~ICInterface(){}好一點點,至少表示C++已經正視預設解構有其需要。
但它還是讓ICInterface貼了一片不像介面該有的膏葯。
我覺得較好的設計(針對我的問題)就是
純虛擬函式,就是純抽象的,不能再去定義它(只能由繼承類別定義)。
有純虛擬函式的類別就是抽象類別,不能俱現物件。
若是類別內沒有純虛擬函式,而又想宣告類別是抽象的,可以這樣寫:
class A{
//:::::::::::::::::::::::
}=0;

這樣子當類別像這樣:
class IInterface{
virtual ~IInterface()=0;
};
就表明IInterface需要一個預解構式(但邏輯上可以看成沒有解構子,以符合介面的面貎)
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/11 上午 12:59:21
>因為給預設的純虛擬解構式的意思等於 "你拉的屎, 由你的子孫來擦"
所以預設建構式的意思等於:自己的小孩原來是隔壁老王的種? XD
太嚴肅了,輕鬆一下^^
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/11 上午 01:40:15

>//Interface.h
>struct CInterface{
>virtual bool Read(...)=0;
>virtual bool Write(...)=0;
>//<--注意到interface就是interface,沒有建構,也沒解構
>};
>一個帶有 CInterface的類別CInterfaceUSB:
>class CInterfaceUSB
>{
>::::::::::::::::::::::::::::::::::::
>public:
> class CUsb : public CInterface//用巢狀類別實作Interface
> {
>  virtual bool Read(...);
>  virtual bool Write(...);
>  //<--注意喔,還是沒有解構式,interface本來就沒有解不解構的問題
> }m_Usb;
>friend class CUsb ;//使得介面實作可以存取CInterfaceUSB成員
>CInterfaceUSB();//通訊類別有建構式
>virtual ~CInterfaceUSB();//也有解構式
>};
>#define INTERFACE_PROLOGUE(theClass, localClass) \
> theClass* pthis = \
> ((theClass*)((BYTE*)this - offsetof(theClass, m_##localClass)));
>bool CInterfaceUSB::CUsb::Read(...)
>{
>INTERFACE_PROLOGUE(CInterfaceUSB,Usb ) //獲得指向CInterfaceUSB的指標 pthis以便操作物件
>pthis->.......//可以用pthis操作CInterfaceUSB的成員了
>}
>bool CInterfaceUSB::CUsb::Write(...)
>{請參考CInterfaceUSB::CUsb::Read}
>CInterfaceUSB UsbObj;//創建USB通訊物件
>extern 'C' _declspec(dllexport) CInterface * GetInterface()
>{return &UsbObj.m_Usb;}
>
>GetInterface()得到的就是標準的介面,介面的操作就是依CInterface的規格。
>CInterface只是介面規格(操作出入口),不是給客戶的類別繼承或創建物件的。
>這樣的介面有沒有題,沒有!
>它完全合乎介面的要求!(不要再說C++不能實作介面了)
>唯一令人不滿的是,有點給它複雜,從interface轉回真正的類別物件(pthis)會有一點花費。
>最主要的缺點是太複雜,如果一個project需要很多類似的介面,又不需要太嚴格,真的是麻煩。
>所以才想要用實作類別繼承Interface類別的方式來實現Interface。
>這樣做,真的簡單很多(雖然在保護上有不週的地方,但要簡單總得犧牲一些)
>實際上也可行,但就是因為解構子不能'純'抽象化而有原問題的提出...
>這樣問題夠清楚了吧?

你說這都是你寫的? 那就好辦
在 CInterface 多加一個
 void Release() = 0;

然後在 CUsb 的實作只要取得 CInterfaceUSB 的實例, 把它砍了, 那麼 CInterface 的實例也跟著完蛋, 這樣算完美解決了吧 ^^
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/11 上午 10:15:10
>在 CInterface 多加一個
>void Release() = 0;
你是說 virtual void Release() = 0;吧?

>然後在 CUsb 的實作只要取得 CInterfaceUSB 的實例, 把它砍了,
> 那麼 CInterface 的實例也跟著完蛋, 這樣算完美解決了吧 ^^
問題不在砍CInterfaceUSB 的物件的時機,而是除非CInterfaceUSB
沒有虛擬解構子,否則CInterface就要跟著宣告虛擬解子,而做為一個
介面,CInterface不應該有解構子的定義,把它宣告為純虛擬解構子勉予接受,
但因為CInterfaceUSB物件解構的關係,C++還要有CInterface解構式定義。
若再定義了CInterface::~CInterface()就使得CInterface不像個介面了。
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/11 下午 02:03:25

>>在 CInterface 多加一個
>>void Release() = 0;
>你是說 virtual void Release() = 0;吧?
>
>>然後在 CUsb 的實作只要取得 CInterfaceUSB 的實例, 把它砍了,
>> 那麼 CInterface 的實例也跟著完蛋, 這樣算完美解決了吧 ^^
>問題不在砍CInterfaceUSB 的物件的時機,而是除非CInterfaceUSB
>沒有虛擬解構子,否則CInterface就要跟著宣告虛擬解子,而做為一個
>介面,CInterface不應該有解構子的定義,把它宣告為純虛擬解構子勉予接受,
>但因為CInterfaceUSB物件解構的關係,C++還要有CInterface解構式定義。
>若再定義了CInterface::~CInterface()就使得CInterface不像個介面了。
>
我的看法和你不同,你的程式架構正好是解決了 c++ 內定解構子不是 virtual 的好辦法,我簡化說明

class A
{
};

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

A *pA = new B;

delete pA; // 因 A::~A() 不是 virtual,所以 B::~B() 沒法被叫到

而 coco 的程式寫成像這樣

class Container
{
 B *pB;
public:
 Container()
 {
  pB = new B;
 }
 ~Container()
 {
  delete pB; // 本來覺得你的程式架構很奇怪,後來仔細想想它是為了做這個
 }
 A *Get()
 {
  return pB;
 }
};
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/11 下午 02:43:38
>我的看法和你不同,你的程式架構正好是解決了 c++
>內定解構子不是 virtual 的好辦法,我簡化說明
>::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
>A *pA = new B;
>delete pA; // 因 A::~A() 不是 virtual,所以 B::~B() 沒法被叫到
>而 coco 的程式寫成像這樣
>class Container
>{
> B *pB;
>public:
> Container()
> {
>  pB = new B;
> }
> ~Container()
> {
>  delete pB; // 本來覺得你的程式架構很奇怪,後來仔細想想它是為了做這個
> }
> A *Get()
> {
>  return pB;
> }
>};

Container 介面不一樣的應用。
如果A 是提供操作Container的方法,那麼可以說A是Container的介面就沒錯。
但從你的程式看,A和Container扯不上關係的,所以A不是Container的介面。
也就和我的問題搭不上邊了....
 
 
 
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/11 下午 02:53:53

>Container 介面不一樣的應用。
>如果A 是提供操作Container的方法,那麼可以說A是Container的介面就沒錯。
>但從你的程式看,A和Container扯不上關係的,所以A不是Container的介面。
>也就和我的問題搭不上邊了....

把 A 看成 CInterface
Container 看成 CInterfaceUSB
不就一樣了
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/11 下午 09:49:47
>把 A 看成 CInterface
>Container 看成 CInterfaceUSB
>不就一樣了
這麼簡單一語帶過,CInterfaceUSB何必搞得希奇古怪的。
A既是Container的介面,那它要如何存取Container呢?
Container的指標再丟進pB,讓pB去操作Container嗎?
那還是需要設friend給pB,不是不可行,也不能說A不是介面。
正如前面我說的,介面是一個概念,做得合乎這個概念,就是
實現了介面功能。
但介面的實現方法有好有壞,從物件導向的觀點來比較:
從封裝性來看:
class CUsb是巢狀類別,封裝在CInterfaceUSB裡面;
class B 則沒有封裝性,它可能被用在Container之外。
從綁定性來看:
class B的物件m_Usb內嵌在CInterfaceUSB裡面,使得它不但明示
了m_Usb的專用性,它要存取所屬的CInterfaceUSB只需一個固定的offfset
即可存取。
class B 的物件 pB不是"天生"可存取到Container,需要Container指標傳入。
這種綁定的方法,也常用,正規的名稱叫Attach。
但Attach有其應用範圍,用在這裡和內嵌比較,它需外力介入(不是天生), 
外力通常伴隨需要正確的時間點介入,容易漏失或時間點不對.
以上兩點足以顯示CInterfaceUSB為什麼要搞得有點複雜,不是像Container看
來簡單。

另外一提,你程式的那個delete pB是多餘的,成員class B 物件可以讓它自生自滅即可:
class Container
{
 B b;//<--自然生最健康了
public:
 Container()
 {
  //pB = new B;//<--不要剖腹了
 }
 ~Container()
 {
  //delete pB; //自然生就不用花力氣縫肚皮善後了
 }
 A *Get()
 {
  return &b;
 }
};
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/12 上午 12:49:50
不清楚你哪裡還不懂,我實際寫個例子,和你寫的架構差不多

#include <iostream>
#include <string>

using namespace std;

class Container;
Container *pthis;

// 這樣介面夠乾淨了吧
class IA
{
public:
 virtual void WhoR(const string &Name) = 0;
 virtual void Release() = 0;
};

class CA: public IA
{
 string mName;
public:
 void WhoR(const string &Name)
 {
  mName = Name;
 }

 void Release();

 ~CA()
 {
  cout << mName << ", Bye Bye." << endl;
 }
};

class Container
{
 CA mA;
public:
 IA &Get()
 {
  return mA;
 }
};

void CA::Release()
{
 delete pthis;
 pthis = NULL;
}


IA &GetIA()
{
 pthis = new Container;
 return pthis->Get();
}

int main()
{
 string Name;
 cout << "Who are you: ";
 cin >> Name;
 IA &A = GetIA();
 A.WhoR(Name);
 A.Release();

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

#include <iostream>
#include <string>

using namespace std;

class Container;
Container *pthis;


// 這樣介面夠乾淨了吧
class IA
{
public:
  virtual void WhoR(const string &Name) = 0;
  virtual void Release() = 0;
};


class Container
{
  class CA: public IA
  {
    string mName;
  public:
    void WhoR(const string &Name)
    {
     mName = Name;
    }

    void Release()
    {
     delete pthis;
     pthis = NULL;
    }

    ~CA()
    {
     cout << mName << ", Bye Bye." << endl;
    }
  }mA;


public:
  IA &Get()
  {
    return mA;
  }
};


IA &GetIA()
{
  pthis = new Container;
  return pthis->Get();
}

int main()
{
  string Name;
  cout << "Who are you: ";
  cin >> Name;
  IA &A = GetIA();
  A.WhoR(Name);
  A.Release();

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

#include <iostream>
#include <string>

using namespace std;

class Container;
Container *pthis;


// 這樣介面夠乾淨了吧
class IA
{
public:
 virtual void WhoR(const string &Name) = 0;
 virtual void Release() = 0;
};


class Container
{

 class CA: public IA
 {
  string mName;
 public:
  void WhoR(const string &Name)
  {
   mName = Name;
   pthis->CallContainer(Name);
  }

  void Release()
  {
   delete pthis;
   pthis = NULL;
  }

  ~CA()
  {
   cout << mName << ", Bye Bye." << endl;
  }
 }mA;

 void CallContainer(const string &Name)
 {
  cout << "I see. You are " << Name << '.' << endl;
 }

public:
 IA &Get()
 {
  return mA;
 }

 virtual ~Container()
 {

 }

 friend class CA;
};


IA &GetIA()
{
 pthis = new Container;
 return pthis->Get();
}

int main()
{
 string Name;
 cout << "Who are you: ";
 cin >> Name;
 IA &A = GetIA();
 A.WhoR(Name);
 A.Release();

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

>這麼簡單一語帶過,CInterfaceUSB何必搞得希奇古怪的。
>A既是Container的介面,那它要如何存取Container呢?
>Container的指標再丟進pB,讓pB去操作Container嗎?
>那還是需要設friend給pB,不是不可行,也不能說A不是介面。
>正如前面我說的,介面是一個概念,做得合乎這個概念,就是
>實現了介面功能。
>但介面的實現方法有好有壞,從物件導向的觀點來比較:
>從封裝性來看:
>class CUsb是巢狀類別,封裝在CInterfaceUSB裡面;
>class B 則沒有封裝性,它可能被用在Container之外。
>從綁定性來看:
>class B的物件m_Usb內嵌在CInterfaceUSB裡面,使得它不但明示
>了m_Usb的專用性,它要存取所屬的CInterfaceUSB只需一個固定的offfset
>即可存取。
>class B 的物件 pB不是'天生'可存取到Container,需要Container指標傳入。
>這種綁定的方法,也常用,正規的名稱叫Attach。
>但Attach有其應用範圍,用在這裡和內嵌比較,它需外力介入(不是天生), 
>外力通常伴隨需要正確的時間點介入,容易漏失或時間點不對.
>以上兩點足以顯示CInterfaceUSB為什麼要搞得有點複雜,不是像Container看
>來簡單。

邊看你的講解一邊做,越做越像樣了,融合多項技術,值得珍藏 ^^

#include <iostream>
#include <string>

using namespace std;


// 這樣介面夠乾淨了吧
class IA
{
public:
 virtual void WhoR(const string &Name) = 0;
 virtual void Release() = 0;
};


class Container
{
 class CA: public IA
 {
  string mName;
 public:
  void WhoR(const string &Name)
  {
   mName = Name;
   ((Container*)this)->CallContainer(Name);
  }

  void Release()
  {
   delete this;
  }

  ~CA()
  {
   cout << mName << ", Bye Bye." << endl;
  }
 }mA;

 void CallContainer(const string &Name)
 {
  cout << "I see. You are " << Name << '.' << endl;
 }

public:
 IA &Get()
 {
  return mA;
 }

 Container()
 {
 }

 virtual ~Container()
 {

 }

 friend class CA;
};


IA &GetIA()
{
 Container *pthis = new Container;
 return pthis->Get();
}

int main()
{
 string Name;
 cout << "Who are you: ";
 cin >> Name;
 IA &A = GetIA();
 A.WhoR(Name);
 A.Release();

 return 0;
}

作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/12 上午 01:44:15
>class Container
>{
> class CA: public IA
> {
> string mName;
> public:
> void WhoR(const string &Name)
> {
> mName = Name;
> }
>::::::::::::::::::::::::::::::::::::::::
> }mA;
mA作為 Container 的介面,就要能操作到Container的成員。
只是存取到mA自己的成員(mName)還沒有完成介面的功能喔。
不過就差一步了。另外
>void Release()
> {
> delete pthis;
> pthis = NULL;
> }
Release()函式 delete 掉一個global 的Container 物件,是一個
失控的OOP。一般Container的設計是這樣的:
IA *CreateContainer() //建立物件是全域或export函式
{
return new Container();//建立Container物件並返回其介面
}

void CA::DeleteContainer()//可以透過CreateContainer() 得到的介面,把對應的Container物
{
return new Container();
}
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/12 上午 01:51:51
前篇還沒編輯完成,從void CA::DeleteContainer()那一行修正起:
void CA::DeleteContainer()
{//可以透過CreateContainer() 得到的介面,把對應的Container物件摧毁
:::::::://這裡要怎麼做,等你把所差的那一步補齊了,再告訴你(其實補了那一小步,怎麼做也很清楚了)
}

當你做完這些,就已和COM的精髓差不多了,但也幾乎和我的CInterfaceUSB是一樣的東西了^^
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/12 上午 01:55:39

>>class Container
>>{
>> class CA: public IA
>> {
>> string mName;
>> public:
>> void WhoR(const string &Name)
>> {
>> mName = Name;
>> }
>>::::::::::::::::::::::::::::::::::::::::
>> }mA;
>mA作為 Container 的介面,就要能操作到Container的成員。
>只是存取到mA自己的成員(mName)還沒有完成介面的功能喔。
>不過就差一步了。另外
>>void Release()
>> {
>> delete pthis;
>> pthis = NULL;
>> }
>Release()函式 delete 掉一個global 的Container 物件,是一個
>失控的OOP。一般Container的設計是這樣的:
>IA *CreateContainer() //建立物件是全域或export函式
>{
>return new Container();//建立Container物件並返回其介面
>}
>
>void CA::DeleteContainer()//可以透過CreateContainer() 得到的介面,把對應的Container物
>{
>return new Container();
>}
>

搞不清楚你為什麼要搞得那麼複雜, 把要放到 Container 的程式碼, 直接放在 CA 不就好了
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/12 上午 02:23:56

>前篇還沒編輯完成,從void CA::DeleteContainer()那一行修正起:
>void CA::DeleteContainer()
>{//可以透過CreateContainer() 得到的介面,把對應的Container物件摧毁
>:::::::://這裡要怎麼做,等你把所差的那一步補齊了,再告訴你(其實補了那一小步,怎麼做也很清楚了)
>}
>
>當你做完這些,就已和COM的精髓差不多了,但也幾乎和我的CInterfaceUSB是一樣的東西了^^

我最後想到的辦法是, 不用記錄 Container, 因 IA 已和 Container 結合了, 所以使用端只要呼叫 Release() 就把自己刪了, 不過考慮到 Container 可能會有東西,所以要修正一下

  void Release()
  {
   delete (Container*)this;
  }

你大概說的就是這了吧

只是這種架構應算是 c++ 多重繼承的變型, 這樣做有比多重繼承優嗎
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/12 上午 02:29:14
>搞不清楚你為什麼要搞得那麼複雜, 把要放到 Container 的程式碼, 直接放在 CA 不就好了
不曉得該哭還是笑...回到原點了。
就是在某些介面的輕度應用,不希望動用到CInterfaceUSB這樣的重型機具。
才會發這帖牢騷問題。
"把要放到 Container 的程式碼, 直接放在 CA"就是沒有Container。
IA就是CA的界面,是吧!
而CA繼承IA是吧!
CA(或其繼承類別)如果需要虛擬解構子,那麼IA也要虛擬解構子,IA的解構子又不能純抽象化
(也不給預設),IA開始不像介面了....這些已經重覆說明多次了
知道為我最原始的問題了嗎?
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/12 上午 02:46:34

>>搞不清楚你為什麼要搞得那麼複雜, 把要放到 Container 的程式碼, 直接放在 CA 不就好了
>不曉得該哭還是笑...回到原點了。
>就是在某些介面的輕度應用,不希望動用到CInterfaceUSB這樣的重型機具。
>才會發這帖牢騷問題。
>'把要放到 Container 的程式碼, 直接放在 CA'就是沒有Container。
>IA就是CA的界面,是吧!
>而CA繼承IA是吧!
>CA(或其繼承類別)如果需要虛擬解構子,那麼IA也要虛擬解構子,IA的解構子又不能純抽象化
>(也不給預設),IA開始不像介面了....這些已經重覆說明多次了
>知道為我最原始的問題了嗎?
>
知道了, 所以我才提出用 Release() 穿越 IA 來到 CA, 就不須要把 IA 的解構子 virtual
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/12 上午 10:12:59
>知道了, 所以我才提出用 Release() 穿越 IA 來到 CA, 就不須要把 IA 的解構子 virtual
不,不論你怎麼穿越,只要換起 CA的解構式,最終會到IA的解構式(這不是你前面諄諄教誨嗎)
只要CA的解構子是virtual,IA就一定要有虛擬解構子,而且一定要有定義,麻煩在這。
作者 : sunyear(coco) VC++卓越專家C++頂尖高手貼文超過2000則
[ 貼文 2421 | 人氣 1485 | 評價 6060 | 評價/貼文 2.5 | 送出評價 5 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/12 上午 11:46:37
大概想通一些東西了。
Interface 是可以宣告純抽象解構子的:
struct IInterface{
virtual void method1()=0;
virtual void method1()=0;
:::::::::::::::::::::::::::::::::::::::::::
virtual ~IInterface()=0;
};
對客戶端的程式,並不需要實作(定義)IInterface::~IInterface()
這就相當於宣告~IInterface()是一個純抽象的解構式。
因為客戶端的程式不會有class繼承IInterface(界面類別不是給客戶的基類別)。
所以不會有問題(感覺舒坦了^^)。
Interface的實作端:
class CInterfaceUSB: public IInterface
{
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
virtual ~CInterfaceUSB(){}
};
IInterface::~IInterface(){}//<--~IInterface()定義在伺服端,OK的!CInterfaceUSB本來就要實作定義
這樣看起來應該 OK的,算是一種輕量級的Interface實現^^(可以笑了)

唯一的問題是:若是CInterfaceUSB 和 IInterface在同一個程式模組會怎樣?
問題好像又出現了 :(
不是這樣,本來界面的目的是在兩座山(客戶端和伺服端)塔橋,
若是兩座山合併成一座山(客戶端和伺服端在同一個程式),含有伺服功能
的程式本來就應該有CInterfaceUSB的建構和解構,需有IInterface::~IInterface()
的定義也是合理。
只是IInterface不要這樣寫:
struct IInterface{
:::::::::::::::::::::::::::::::::::::::::::
virtual ~IInterface(){};
};
而是解構子,還是應該維持純抽象的性質:
struct IInterface{
:::::::::::::::::::::::::::::::::::::::::::
virtual ~IInterface()=0;
};

IInterface::~IInterface()的定義,應該還是放在另一個.c程式檔,
例如InterfaceUSB.c

我一直在一座山想兩座山的事,導致參透這件事遲頓了。
現在可以安心睡覺了 XD
作者 : cxxlman(CxxlMan) C++優秀好手貼文超過1000則
[ 貼文 1042 | 人氣 3227 | 評價 1260 | 評價/貼文 1.21 | 送出評價 28 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/1/12 下午 07:55:03

>>知道了, 所以我才提出用 Release() 穿越 IA 來到 CA, 就不須要把 IA 的解構子 virtual
>不,不論你怎麼穿越,只要換起 CA的解構式,最終會到IA的解構式(這不是你前面諄諄教誨嗎)
>只要CA的解構子是virtual,IA就一定要有虛擬解構子,而且一定要有定義,麻煩在這。
>

不知你在想什麼,我再修正確一點,注意 ~CA() 我故意加上 virtual

只是寫 C++ 就得像個 C++ 咩,IA 有 Constructor 和 Destructor 又何妨

#include <iostream>
#include <string>

using namespace std;

// 這樣介面夠乾淨了吧
class IA
{
public:
 virtual void WhoR(const string &Name) = 0;
 virtual void Release() = 0;
};

class Container
{
 class CA: public IA
 {
  Container *pthis;
  string mName;
 public:
  void WhoR(const string &Name)
  {
   mName = Name;
   pthis->CallContainer(Name);
  }

  void Release()
  {
   delete pthis;
  }

  CA(Container *pContainer)
  {
   pthis = pContainer;
  }

  virtual ~CA() // 這裡是 virtual 喔, IA 仍不用改變
  {
   cout << mName << ", Bye Bye." << endl;
  }
 }mA;

 void CallContainer(const string &Name)
 {
  cout << "I see. You are " << Name << '.' << endl;
 }

public:
 IA &Get()
 {
  return mA;
 }

 Container()
  :mA(this)
 {
 }

 virtual ~Container()
 {

 }

 friend class CA;
};


IA &GetIA()
{
 Container *pthis = new Container;
 return pthis->Get();
}

int main()
{
 string Name;
 cout << "Who are you: ";
 cin >> Name;
 IA &A = GetIA();
 A.WhoR(Name);
 A.Release();

 return 0;
}
 板主 : 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.375