【问题标题】:How can I get polymorphic behavior in a C++ constructor?如何在 C++ 构造函数中获得多态行为?
【发布时间】:2009-09-21 06:42:40
【问题描述】:

我有一个我希望看起来像这样的基类:

class B
{
    // should look like: int I() { return someConst; }
    virtual int I() = 0;
    public B() { something(I()); }
}

重点是强制派生类覆盖I,并在构造每个对象时强制调用它。这习惯于做一些簿记,我需要知道正在构造什么类型的对象(但我将当前对象视为基类)。

这不起作用,因为 C++ 不允许您从构造函数调用抽象虚函数。

有没有办法达到同样的效果?


基于this link,答案似乎是没有办法得到我想要的。但是它说的是:

简短的回答是:不。基类对派生自哪个类一无所知——这也是一件好事。 [...] 也就是说,直到构造函数 Derived1::Derived1 开始,对象才正式成为 Derived1 的实例。

但就我而言,我不想知道它是什么,而是会变成什么。事实上,只要用户可以(事后)将它映射到一个类,我什至不在乎我得到了什么。所以我什至可以使用返回指针之类的东西并摆脱它。

(现在回到阅读该链接)

【问题讨论】:

标签: c++ abstract virtual-functions


【解决方案1】:

您不能从构造函数中调用虚方法(或者更准确地说,您可以调用它们,但最终会从当前正在构造的类中调用成员函数)。 ,问题是派生对象此时还不存在。对此您几乎无能为力,从构造函数中多态地调用虚方法是不可能的。

您应该重新考虑您的设计——例如,将常量作为参数传递给构造函数。

class B
{
public:
    explicit B(int i)
    {
        something(i);
    }
};

请参阅C++ faq 了解更多信息。如果你真的想在构造过程中调用虚函数,read this

【讨论】:

  • 在构造函数之后调用 init() 方法是一种解决方法。但是,您需要在构建类的任何地方进行更改 - 如果您使用的是工厂,这很好。
  • C++ FAQ条目不错,阅读对应的FQA条目也不错:yosefk.com/c++fqa/inheritance-mother.html#fqa-23.6
  • 可以在构造函数中调用虚方法。他们只是没有做你期望他们做的事。
  • 您的意思是说“尝试另一种编程语言怎么样?”的 FQA 条目?
  • 这就是我最终要做的事情,幸运的是,我可以进行设置,以便只公开一个基类构造函数,并且只从派生类构造函数调用它。 (并且由于预定义的宏,始终采用完全相同的形式:Base(__SOMEMACRO__)
【解决方案2】:

也许对每个派生类型都使用静态工厂方法?这是在 .NET 中构造奇异对象(阅读:具有非常具体的初始化要求的对象)的常用方法,我对此表示赞赏。

class Base
{
  protected Base(int i)
  {
    // do stuff with i
  }
}

class Derived : public Base
{
  private Derived(int i)
    : Base(i)
  {
  }

  public Derived Create()
  {
    return new Derived(someConstantForThisDerivedType);
  }
}

在基类构造函数中调用虚方法通常是不受欢迎的,因为你永远无法确定特定方法的行为,而且(正如其他人已经指出的那样)派生构造函数还不会被调用。

【讨论】:

    【解决方案3】:

    这将不起作用,因为在执行基类构造函数时派生类还不存在:

    class Base
    {
    public:
        Base()
        {
            // Will call Base::I and not Derived::I because
            // Derived does not yet exist.
            something(I());
        }
    
        virtual ~Base() = 0
        {
        }
    
        virtual int I() const = 0;
    };
    
    class Derived : public Base
    {
    public:
        Derived()
         : Base()
        {
        }
    
        virtual ~Derived()
        {
        }
    
        virtual int I() const
        {
            return 42;
        }
    };
    

    相反,您可以将参数添加到基类构造函数:

    class Base
    {
    public:
        explicit Base(int i)
        {
            something(i);
        }
    
        virtual ~Base() = 0
        {
        }
    };
    
    class Derived : public Base
    {
    public:
        Derived()
         : Base(42)
        {
        }
    
        virtual ~Derived()
        {
        }
    };
    

    或者,如果您真的喜欢 OOP,您还可以创建几个额外的类:

    class Base
    {
    public:
        class BaseConstructorArgs
        {
        public:
            virtual ~BaseConstructorArgs() = 0
            {
            }
    
            virtual int I() const = 0;
        };
    
        explicit Base(const BaseConstructorArgs& args)
        {
            something(args.I());
        }
    
        virtual ~Base() = 0
        {
        }
    };
    
    class Derived : public Base
    {
    public:
        class DerivedConstructorArgs : public BaseConstructorArgs
        {
        public:
            virtual ~DerivedConstructorArgs()
            {
            }
    
            virtual int I() const
            {
                return 42;
            }
        };
    
        Derived()
         : Base(DerivedConstructorArgs())
        {
        }
    
        virtual ~Derived()
        {
        }
    };
    

    【讨论】:

      【解决方案4】:

      您需要的是two-phase construction。使用通用程序员的解决方法:添加另一层间接。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-19
        • 2012-03-09
        • 1970-01-01
        相关资源
        最近更新 更多