【问题标题】:How to pass method result as parameter to base class constructor in C++?如何将方法结果作为参数传递给 C++ 中的基类构造函数?
【发布时间】:2008-12-17 16:31:16
【问题描述】:

我正在尝试实现这样的目标:

class Base
{
  public:

  Base(string S) 
  {
  ...
  };
}

class Derived: Base
{
public:
  int foo;
  string bar()
  {
    return stringof(foo); // actually, something more complex
  };

  Derived(int f) : foo(f), Base(bar()) 
  {
  };
}

现在,这不能如我所愿,因为 bar() 在 foo 初始化之前在 Derived 构造函数中被调用。

我考虑添加一个类似于 bar() 的静态函数,它以 foo 作为参数 - 并在初始化列表中使用它,但我想我会问是否有任何其他技术可以用来让自己摆脱困境这个……

编辑:感谢您的反馈 - 这是我将如何处理静态函数的方法。不确定静态和非静态函数之间的重载是否太聪明了,但是...

class Derived: Base
{
public:
  int foo;

  static string bar(int f)
  {
    return stringof(f); // actually, something more complex
  }

  string bar()
  {
    return bar(foo); 
  };

  Derived(int f) :  Base(bar(f)) , foo(f)
  {
  };
}

【问题讨论】:

    标签: c++ inheritance constructor initialization composition


    【解决方案1】:

    是的,使用以foo为参数并返回字符串的函数(静态类方法或常规函数)是一个很好的解决方案。您可以从 Derived::bar 调用相同的函数来防止代码重复。因此,您的构造函数将如下所示:

    Derived(int f) : Base(stringof(f)), foo(f) {}
    

    我在列表中首先调用 Base 构造函数,以强调初始化发生的顺序。初始化列表的顺序没有影响,因为所有类成员都按照它们在类主体中声明的顺序进行初始化。

    这是一个非常干净的functional 解决问题的方法。但是,如果您仍然想权衡替代方案,请考虑使用 composition 而不是继承来处理 Derived 和 Base 类之间的关系:

    class Base {
    public:
        Base(string S) {  ...  }
        void bat() { ... }
    };
    
    class Derived {
        Base *base;
        int foo;
    
    public:
        Derived(int f) : base(NULL), foo(f) {
            base = new Base(bar());
        }
        ~Derived() {
            delete base;
        }
    
        string bar() {
            return stringof(foo); // actually, something more complex
        }
    
        void bat() {
            base->bat();
        }
    };
    

    您需要根据您的具体情况考虑pros 和缺点。使用 Derived 持有对 Base 的引用,您可以更好地控制初始化顺序。

    【讨论】:

    • 嗯,把它和 pimpl 成语结合起来会很好
    • 很好——但是组合的缺点是需要暴露基本方法的“bat-like”存根的数量。
    • 真的!出于这个原因,我认为在这种情况下你会坚持使用 A 计划。
    【解决方案2】:

    您只能在初始化列表中调用静态函数。您在代码中使用它的方式:

    class Derived: Base
    {
    public:
      int foo;
      string bar()
      {
        return stringof(foo); // actually, something more complex
      };
    
      Derived(int f) : foo(f), Base(bar()) 
      {
      };
    }
    

    仍会先初始化 Base,然后再初始化 foo。在构造函数初始化器列表中编写内容的顺序无论如何都无关紧要。它将始终按此顺序构建:

    1. 首先,所有虚拟基类
    2. 然后是非虚拟基类,按照它们在基类列表中出现的顺序排列
    3. 然后所有成员对象按照它们在类定义中定义的顺序排列。

    因此,您最终会使用未初始化的值调用 stringof。这个问题在boost::base_from_member 中得到解决。另请注意,在所有基类的所有构造函数初始化程序完成之前调用任何非静态成员函数是未定义的行为。

    不过,调用静态函数完全没问题:

    class Derived: Base
    {
    public:
      int foo;
      static string bar(int f)
      {
        return stringof(f); // actually, something more complex
      };
    
      Derived(int f) : Base(bar(f)), foo(f)
      {
      };
    }
    

    【讨论】:

      【解决方案3】:

      基类构造函数总是在初始化派生类的其他成员之前被调用;您的编译器应该警告您初始化程序的顺序错误。唯一正确的解决方案是将bar() 设为以f 为参数的静态方法。

      【讨论】:

        【解决方案4】:

        构造函数是用来构造对象的。这意味着,在它返回之前,那里没有对象,因此调用成员函数不会可靠地工作。正如其他人所说,使用静态函数或非成员函数。

        【讨论】:

        • 严格来说并不正确,因为构造函数调用非静态方法是常见的做法——例如,“init()”。初始化列表有点不同。
        【解决方案5】:

        我也一直想做这个,但最后还是放弃了。
        任何合适的函数调用都可以用作 Base() 的参数。
        另一种选择是向 Base 添加替代构造函数,该构造函数采用 int 并自行转换为“字符串”。

        【讨论】:

        • “替代构造函数”不好,因为转换逻辑基本上是派生类的一部分。
        • 嗯,我猜是这样,但这是我能想到的静态函数的唯一替代方案。
        【解决方案6】:

        只需将您的构造函数代码移动到 Initialize() 函数并从构造函数中调用它。这比静态/非静态覆盖或类似的东西要简单得多。

        【讨论】:

        • 你没有抓住重点。 基类不能按照你描述的方式正确构造。
        猜你喜欢
        • 2014-12-25
        • 1970-01-01
        • 1970-01-01
        • 2023-04-04
        • 2013-08-10
        • 2015-10-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多