【问题标题】:C++ Parent class calling a child virtual functionC++父类调用子虚函数
【发布时间】:2010-09-18 22:09:21
【问题描述】:

我想要一个纯虚拟父类来调用函数的子实现,如下所示:

class parent
{
  public:
    void Read() { //read stuff }
    virtual void Process() = 0;
    parent() 
    {
        Read();
        Process();
    }
}
class child : public parent
{
  public:
    virtual void Process() { //process stuff }
    child() : parent() { }
}

int main()
{
   child c;
}

这应该可以,但我得到一个未链接的错误:/ 这是使用 VC++ 2k3

或者它不应该工作,我错了吗?

【问题讨论】:

    标签: c++ inheritance virtual


    【解决方案1】:

    以下文章的标题说明了一切:Never Call Virtual Functions during Construction or Destruction

    【讨论】:

    • 正确,但有点短。您至少应该复制结论:“要记住的事情:不要在构造或销毁期间调用虚函数,因为此类调用永远不会转到比当前执行的构造函数或析构函数更派生的类。”
    • 不过,我真的认为在通过继承混合功能之前应该三思而后行。恕我直言,这个问题显示了表面下的设计流程。
    【解决方案2】:

    或者,创建一个用于创建对象的工厂方法并将构造函数设为私有,然后工厂方法可以在构造后初始化对象。

    【讨论】:

      【解决方案3】:

      通常可以工作,但不适用于纯虚拟基类的构造函数中的调用。在构造基类时,子类覆盖不存在,因此您不能调用它。只要您在构建整个对象后调用它,它就应该可以工作。

      【讨论】:

        【解决方案4】:

        这是因为你的调用是在构造函数中。派生类在构造函数完成之前是无效的,所以你的编译器是正确的。

        有两种解决方案:

        1. 在派生类的构造函数中调用 Process()
        2. 为 Process 定义一个空白函数体,如下例所示:
        class parent
        {
          public:
            void Read() { //read stuff }
            virtual void Process() { }
            parent() 
            {
                Read();
                Process();
            }
        }
        

        【讨论】:

        • 这很危险,在父级中定义一个空白函数体并在其构造函数中调用它会导致只执行 Process() 的父级部分(即什么都不执行)。他可能希望将调用解析为虚函数,这在构造函数中是不可能的
        • 确实:会调用空白函数。这不是可行的解决方案。
        【解决方案5】:

        再多一步,您就可以引入某种功能,例如

        class parent
        {
            public:
                void initialize() {
                    read();
                    process();
                }
        }
        

        【讨论】:

          【解决方案6】:

          肤浅的问题是您调用了一个未知的虚函数(对象是从父级到子级构造的,vtables 也是如此)。你的编译器警告过你。

          据我所知,基本问题是您尝试通过继承来重用功能。这几乎总是一个坏主意。一个设计问题,可以这么说:)

          本质上,您尝试实例化模板方法模式,以将 whatwhen 分开:首先读取一些数据(以某种方式),然后处理它(在某种方式)。

          这可能会更好地使用聚合:将 Processing 函数赋予 Template 方法以在正确的时间调用。也许您甚至可以为读取功能做同样的事情。

          聚合可以通过两种方式完成:

          1. 使用虚函数(即运行时绑定)
          2. 使用模板(即编译时绑定)

          示例 1:运行时绑定

          class Data {};
          class IReader    { public: virtual Data read()            = 0; };
          class IProcessor { public: virtual void process( Data& d) = 0; };
          
          class ReadNProcess {
          public:
              ReadNProcess( IReader& reader, IProcessor processor ){
                 processor.process( reader.read() );
              }
          };
          

          示例 2:编译时绑定

          template< typename Reader, typename Writer > // definitely could use concepts here :)
          class ReadNProcess {
          public:
               ReadNProcess( Reader& r, Processor& p ) {
                   p.process( r.read() );
               }
          };
          

          【讨论】:

            【解决方案7】:

            你需要包裹在一个对象内部,在对象完全构造后调用虚方法:

            class parent
            {
              public:
                void Read() { /*read stuff*/ }
                virtual void Process() = 0;
                parent()
                {
                    Read();
                }
            };
            
            class child: public parent
            {
              public:
                virtual void Process() { /*process stuff*/ }
                child() : parent() { }
            };
            
            template<typename T>
            class Processor
            {
                public:
                    Processor()
                        :processorObj() // Pass on any args here
                    {
                        processorObj.Process();
                    }
                private:
                    T   processorObj;
            
            };
            
            
            
            
            int main()
            {
               Processor<child> c;
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-01-07
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-05-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多