2008-12-03 65 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异常处理/构造和析构

被部分 构造或部分被毁 将具有其完全构造的子对象的所有 , 即执行析构函数的对象,对于 构造函数已完成执行的子对象 和析构函数尚未开始执行 。

相关问题