成员如何知道它构造在哪个类实例中?

时间:2008-12-03 18:42:15

标签: c++ constructor


    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;
      }
    };


I want to clarify the fact - it is a part of the working code. You can do this in C++. I just want to do it in a more nice way.


I've found a way to get rid of unlock and try block. I've remembered there is such a thing as thread local storage. Now I can write constructor as simple as that:

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


to dribeas: My objective is to avoid redundant and tedious typing. Without some tricks behind the scene I will have to type for each Field and Param:

这是多余,丑陋和不方便的。例如,如果我必须在中添加新字段 在SELECT的中间我将不得不重写所有的列号。 关于你帖子的一些注意事项:

  1. 字段和参数 由其默认构造函数初始化。
  2. 构造函数中的初始值设定项的顺序无关紧要。字段始终按其声明的顺序初始化。我已经使用这个事实来追踪字段的列索引
  3. 首先构建基类。因此,当构造Fields时,Recordset中的内部字段列表已准备好由Filed默认构造函数使用。
  4. 我不能在这里使用RAII。我需要在Recorset构造函数中获取锁,并在构造所有Fields之后在TestRecordset构造函数中释放它

5 个答案:

答案 0 :(得分:8)

没有。对象不应该知道它们的使用位置才能工作。就x而言,它是T的一个实例。就是这样。根据它是C类的成员,D类的成员,自动的,临时的等等,它的行为不同。

此外,即使T构造函数确实知道C的实例,C的实例也不完整,因为它当然还没有完成构造,因为它的成员尚未构造。 C ++为你提供了很多机会射击自己,但是在另一个类的构造函数中提供一个不完整对象的引用并不是其中之一。

我能想到的唯一能够近似你的代码示例的是做类似

的事情
#define INIT_FIELDS field1(this), field2(this), field3(this)

紧跟在字段列表之后,然后在初始化列表中使用INIT_FIELDS并#undef它。它仍然是重复,但至少它在一个地方。但是,这可能会让你的同事感到惊讶。

确保不忘记字段的另一种方法是从Field中删除zero-arg构造函数。同样,您仍然需要进行输入,但至少如果您忘记了编译器将捕获它的内容。我认为,初始化程序列表的非DRY特性是C ++必须要使用的东西。

答案 1 :(得分:2)

再加上One by One的回答,你应该问的实际问题是:“我的解决方案设计出了什么问题,它需要对象知道它们在哪里被实例化?”

答案 2 :(得分:1)

我不这么认为。

出于纯粹的好奇心,为什么重要?你有一个有用的背景吗?

微米。

答案 3 :(得分:0)

我一直在C#中尝试这样的事情 - 我用反射来做它。

考虑为C ++获取反射或代码生成库,以帮助您按照自己的意愿行事。

现在,我不能告诉你如何为C ++找到一个好的反射或代码生成库,但这是一个不同的问题!

答案 4 :(得分:0)

我对您的代码感兴趣。您评论所有字段加上param属性都有指针返回TestRecordSet但是它们不需要初始化?或者它是问题的对象,如何避免在构造过程中传递这个指针?

如果你想要的是避免在构造函数的初始化列表中添加所有字段,那么它是一个有缺陷的目标。您应该始终初始化初始化列表中的所有成员,并按照在类中声明的顺序执行此操作(这不是语言强制执行,而是更多全局学习经验)。

你对try构造函数块的使用仅仅是他推荐使用该功能(任何感兴趣的人阅读GOTW#66),如果确实需要的话。如果已经构造了RecordSet成员(并因此获得了锁)并且之后在构造函数中出现了问题,那么[见下面的引用] RecordSet将被销毁,如果它在内部使用RAII,它将释放锁,所以我相信可能不需要try / catch。

C ++ 03,15.2异常处理/构造函数和析构函数

  

部分对象   建造或部分毁坏   将为所有人执行析构函数   其完全构建的子对象,   也就是说,对于其中的子对象   构造函数已完成执行   析构函数还没有开始   执行。