【问题标题】:Call a base class constructor later (not in the initializer list) in C++稍后在 C++ 中调用基类构造函数(不在初始化列表中)
【发布时间】:2011-04-18 18:37:14
【问题描述】:

我正在继承一个类,我想调用它的一个构造函数。但是,在调用它之前,我必须处理一些东西(不需要任何基类)。有什么办法我可以稍后调用它而不是在初始化列表中调用它?我相信这可以在 Java 和 C# 中完成,但我不确定 C++。

我需要在构造函数上传递的数据以后不能重新赋值,所以我不能只是调用一个默认构造函数并在以后初始化它。

【问题讨论】:

    标签: c++ constructor delayed-execution


    【解决方案1】:

    有什么方法可以让我稍后调用它而不是在初始化列表中调用它?

    不,你不能。基类构造函数必须在初始化列表中调用,而且必须先调用。

    实际上,如果您在此处省略它,编译器只会隐式添加调用。

    我相信这可以在 Java 和 C# 中完成,但我不确定 C++。

    C# 和 Java 都不允许这样做。

    然而,您可以做的是调用基类构造函数调用的方法作为参数。然后在构造函数之前处理它:

    class Derived {
    public:
        Derived() : Base(some_function()) { }
    
    private:
        static int some_function() { return 42; }
    };
    

    【讨论】:

    • 谢谢,没想到只调用一个函数,这样可以,但是会有点乱,因为构造函数有 2 个参数。我会尽可能接受这个答案(当然,除非出现更好的答案)。顺便说一句,你不能在 Java 和 C# 中做到这一点是对的,我认为这在 Java 中是可能的,因为它是在方法体上使用 super(...) 完成的,但我现在注意到它必须是第一行.
    • +1 您应该将 some_function() 设为静态以记录它不使用类的任何实例变量(尚未初始化)的事实。
    • 有趣,以前从未见过。我假设调用的函数可以是派生类函数。
    • @Patrick:可以派生,但不能是虚拟的,不能访问成员变量或this指针。
    • @Patrick:编译器不一定会警告你,但它确实是一个硬限制,因为对象还不存在,所以它的成员也不存在。在调用构造函数之前,它们的值只是内存垃圾,另外你甚至不能真正访问它们,因为this 指针还不存在,而这是计算成员变量的位置所需要的。
    【解决方案2】:

    正如几个回答的人所说,您不能延迟基类构造函数的调用,但Konrad has given a good answer 可能会很好地解决您的问题。但是,这确实有其缺点(例如,当您需要使用其计算共享中间结果的值来初始化多个函数时),所以为了完整起见,这是解决固定初始化顺序问题的另一种方法,使用 它。

    鉴于初始化的固定顺序,如果您可以控制派生类(否则您将如何摆弄它的一个 ctor?),您可以潜入一个私有基础,以便对其进行初始化在另一个基础之前,然后可以使用私有基础的已计算值对其进行初始化:

    class my_dirty_little_secret {
      // friend class the_class;
    public: 
      my_dirty_little_secret(const std::string& str)
      {
        // however that calculates x, y, and z from str I wouldn't know
      }
      int x;
      std::string y;
      float z;
    };
    
    class the_class : private my_dirty_little_secret // must be first, see ctor
                    , public the_other_base_class {
      public:
        the_class(const std::string str)
          : my_dirty_little_secret(str)
          , the_other_base_class(x, y, z)
        {
        }
      // ...
    };
    

    my_dirty_little_secret 类是一个私有基类,因此the_class 的用户不能使用它,它的所有内容也是私有的,明确的友谊只授予the_class 访问它。但是,由于它在基类列表中列在首位,因此可以可靠地在 the_other_base_class 之前构造它,因此无论它计算什么都可以用来初始化它。
    基类列表中的一个很好的注释有望防止其他人通过重构来破坏事物。

    【讨论】:

      【解决方案3】:

      恕我直言,我认为不可能以您提到的方式推迟调用基类构造函数。

      【讨论】:

        【解决方案4】:

        哇,我们都曾经年轻过。这个答案不起作用,所以不要使用它。内容留作历史用途。

        如果您可以完全控制基类,我建议您添加一个受保护的方法来进行类初始化,使其成为虚拟,并在调用其基类之前将您的派生类实现细节放入其中:

        class Base
        {
        public:
            Base()
            {
                Initialize();
            }
        protected:
            virtual void Initialize()
            {
                //do initialization;
            }
        };
        
        class Derived : Base
        {
        public:
        
            Derived() : Base()
            {
            }
        protected:
            virtual void Initialize()
            {
                //Do my initialization
                //call base
                Base::Initialize();
            }
        };
        

        【讨论】:

        • 很遗憾,我无法控制基类。
        • 你可能无法实现你的目标。如果您发布更多代码,我们可能会想出一些东西,但我从未见过用 C++ 完成过。
        • 我不喜欢这个主意。两阶段构建总是容易出错。
        • @sbi:随​​意“不喜欢它”...但是如果您需要在基类初始化之前访问 c++ 中的成员数据,这几乎是您唯一的选择。至少我知道的唯一一个。我希望有一个更清洁的解决方案,但这是我所知道的最清洁的解决方案。提问者可能不需要成员数据访问权限,但遇到此问题的其他读者可能需要,而且它比向 IMO 类添加静态函数要干净得多。
        • 这种方法存在很大的问题。在 C++ 中,您不能在构造函数中使用虚函数。在基类构造函数中,对象还没有“成熟”到其派生类的类型。如果您从那里调用虚函数,它将调用该函数的基类版本。
        【解决方案5】:

        根据@Konrad 的建议,另一种选择是使用静态方法来构造对象,例如:

        class Derived {
        public:
            Derived(int p1, int p2, int p3) : Base(p1, p2) { }
        
            static Derived* CreateDerived(int p3) { return new Derived(42, 314, p3); }
        };
        

        我发现这在从库中扩展类并需要覆盖多个参数时很有用。 你甚至可以让构造函数private

        【讨论】:

          【解决方案6】:
          struct base{
             base(int x){}
          };
          
          struct derived : base{
             derived(int x) : base(x){}
          };
          

          这就是在 C++ 中从派生类的初始化列表中调用基类构造函数的方式。

          【讨论】:

          • 这不是他要问的。
          猜你喜欢
          • 1970-01-01
          • 2020-10-07
          • 2018-07-19
          • 1970-01-01
          • 2012-10-23
          • 2017-03-07
          • 2011-01-06
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多