2008-12-03 63 views
0

    class C { 
    public 
     T x; 
    }; 

有沒有一種優雅的方式讓x的構造函數知道暗含地它正在構造的C的實例中? 成員怎樣才能知道它構造的類實例?


我已經用一些骯髒不雅的機器實現了這樣的行爲。我需要這個爲我的sqlite3包裝。我不喜歡我見過的所有包裝,他們的API IMO醜陋和不方便。我想是這樣的:


    class TestRecordset: public Recordset { 
    public: 
     // The order of fields declarations specifies column index of the field. 
     // There is TestRecordset* pointer inside Field class, 
     // but it goes here indirectly so I don't have to 
     // re-type all the fields in the constructor initializer list. 
     Field<__int64> field1; 
     Field<wstring> field2; 
     Field<double> field3; 

     // have TestRecordset* pointer too so only name of parameter is specified 
     // in TestRecordset constructor 
     Param<wstring> param; 

     virtual string get_sql() { 
     return "SELECT 1, '1', NULL FROM test_table WHERE param=:PARAM"; 
     } 

     // try & unlock are there because of my dirty tricks. 
     // I want to get rid of them. 

     TestRecordset(wstring param_value) 
     try : Recordset(open_database(L"test.db")), param("PARAM") { 
     param = param_value; 
     // I LOVE RAII but i cant use it here. 
     // Lock is set in Recordset constructor, 
     // not in TestRecordset constructor. 
     unlock(this); 
     fetch(); 
     } catch(...) { 
     unlock(this); 
     throw; 
     } 
    }; 

我想澄清一個事實 - 它是工作代碼的一部分。你在可以在C++中做到這一點。我只是想以更好的方式做到這一點。


我發現了一種擺脫解鎖和嘗試塊的方法。我記得有線程本地存儲這樣的東西。現在,我可以寫構造器那樣簡單:

 TestRecordset(wstring param_value): Recordset(open_database(L"test.db")), param("PARAM") { param = param_value; fetch(); } 


到dribeas: 我的目標是避免重複和繁瑣的打字。如果沒有幕後的一些技巧,我將不得不鍵入每個字段和帕拉姆:

 
TestRecordset(wstring param_value): Recordset(open_database(L"test.db")), param(this, "PARAM"), 
    field1(this, 0), field2(this, 1), field3(this, 2) { ... } 

這是多餘的,難看又不方便。例如,如果我不得不在SELECT的中間添加新字段,我將不得不重寫所有列號。 您的文章的一些注意事項:

  1. 領域,而params 通過其默認的構造函數初始化。
  2. 構造函數中初始化符的順序是不相關的。字段總是按其聲明的順序進行初始化。我用這個事實來追蹤字段的列索引
  3. 基類是首先構造的。因此,當Fields被構建時,Recordset中的內部字段列表已準備好由Filed默認構造函數使用。
  4. 我不能在這裏使用RAII。我需要在Recorset構造函數中獲取鎖定,並在構造完所有Fields後,在TestRecordset構造函數中釋放它。
+0

我實際上寫了一個答案,抱怨你在構造函數中缺少RAII,但之後當我注意到基類構造函數調用時刪除了它。 – 2008-12-03 20:39:31

+0

另一方面,在RecordSet構造函數中解鎖也不是更好嗎? – 2008-12-03 20:40:27

+0

不需要。我需要在Recordset構造函數中使用qcquire鎖,並在構造完所有Fileds和Params後,在TestRecordset構造函數中強制釋放鎖。 – 2008-12-04 09:29:01

回答

8

編號對象不需要知道他們在哪裏使用以便工作。就x而言,它是T的一個實例。就是這樣。根據它是C類的成員,D類的成員,自動的還是臨時的等等,它的行爲並不不同。此外,即使T構造函數確實知道C的實例,也不會C的實例當然還不完整,因爲它的成員還沒有建成。 C++爲你提供了足夠的機會讓自己在腳下開槍,但是提供給另一個類的構造函數中不完整對象的引用不是其中之一。

我能想到的接近你的代碼示例中的唯一的事情就是字段列表後,立即做這樣的事情

#define INIT_FIELDS field1(this), field2(this), field3(this) 

,然後在初始化列表中使用INIT_FIELDS和#undef它。它仍然是重複的,但至少它在一個地方。但是,這可能會讓你的同事感到驚訝。

另一種確保不忘記字段的方法是從Field中刪除零參數構造函數。同樣,你仍然需要進行打字,但至少如果你忘記了編譯器會抓住它的東西。初始化程序列表的非DRY特性,我認爲,C++必須與之共存。

+0

否 - 否 - 否,此#define是醜陋 2008-12-03 19:36:55

1

我不這麼認爲。

出於純粹的好奇心,爲什麼要重要?你有一個可以有用的背景嗎?

M.

2

添加到一個一個的回答,你應該問的實際問題是:「有什麼不對我的解決方案的設計,它需要的對象知道它們的實例化」

0

我在C#中一直試用這樣的東西 - 我使用反射來做到這一點。

考慮爲C++獲取反射或代碼生成庫來幫助你做你想做的事情。

現在,我不能告訴你如何爲C++找到一個好的反射或代碼生成庫,但這是一個不同的問題!

0

我對你的代碼感興趣。你評論說所有的字段加上param屬性都有指向TestRecordSet的指針,但是它們不需要被初始化?或者這是問題的目的,如何避免在施工過程中必須通過這些指針?

如果你想避免在構造函數的初始化列表中添加所有字段,那麼這是一個有缺陷的目標。你應該始終初始化初始化列表中的所有成員,並按照它們在類中聲明的順序進行初始化(這不是強制執行的語言,而是更多的全球學習體驗)。

如果確實需要,則您使用try構造函數塊的用途僅限於他對該功能的推薦使用(任何感興趣的讀者可以閱讀GOTW#66)。如果RecordSet成員已被構造(並且因此獲得了鎖),並且在構造方法中出現了錯誤,那麼RecordSet將被銷燬,如果它在內部使用RAII則釋放鎖,所以我相信嘗試/抓住可能不是真的需要。

C++ 03,15.2異常處理/構造和析構

被部分 構造或部分被毀 將具有其完全構造的子對象的所有 , 即執行析構函數的對象,對於 構造函數已完成執行的子對象 和析構函數尚未開始執行 。

相關問題