【问题标题】:Named constructor and inheritance命名构造函数和继承
【发布时间】:2009-02-12 08:42:49
【问题描述】:

我正在研究 C++ 框架,并希望将自动内存管理应用于许多核心类。到目前为止,我的标准方法是

class Foo 
{

public:

  static
  shared_ptr<Foo> init() 
  {
    return shared_ptr<Foo>(new Foo);
  }

  ~Foo() 
  {
  }

protected:

  Foo()
  {
  }

};


// Example of use
shared_ptr<Foo> f = Foo::init();

但是,当我将 Foo 子类化时,上述内容会中断,因为即使 init() 被继承,它仍然返回包含指向 Foo 实例的指针的 shared_ptr&lt;Foo&gt;

谁能想到一个优雅的解决方案?我是否应该坚持使用shared_ptr (半)手动包装类实例?这也可以在不声明新的命名构造函数的情况下公开参数化构造函数...

即。

template <typename T>
shared_ptr<T> make_shared(T* ptr)
{
  return shared_ptr<T>(ptr)
}

// Example
shared_ptr<T> 
  f1 = make_shared(new Foo()),
  f2 = make_shared(new Foo(1,2));

【问题讨论】:

    标签: c++ inheritance constructor factory named


    【解决方案1】:

    我会尝试这样的事情:

    template<class T>
    class creator
    {
      public:
        static shared_ptr<T> init()
        {
          return(shared_ptr<T>(new T));
        }
    };
    
    class A : public creator<A>
    {
    };
    
    class B : public A, public creator<B>
    {
      public:
        using make_shared<B>::init;
    };
    
    // example use
    shared_ptr<A> a = A::init();
    shared_ptr<B> b = B::init();
    

    但与您提出的独立模板相比,这不一定能为您节省一件事。

    编辑:我错过了以前的答案,这似乎是同一个想法。

    【讨论】:

      【解决方案2】:

      我不明白这实现了什么,您似乎没有使用这个 init 函数获得任何额外的内存管理,而不是简单地声明一个 shared_ptr。

      int main( void )
      {
          shared_ptr<foo> a = foo::init();
          shared_ptr<foo> b( new foo );
      }
      

      有什么不同。 shared_ptr 提供内存管理,而不是 init 中的任何内容。

      【讨论】:

      • 如果 'shared_ptr' 具有显式构造函数,那么您的第二个示例将无法编译,因为“foo*”不会隐式转换为“shared_ptr”。 (这将适用于 VC,但它是由于 VC 中的一个错误而不是因为它是合法的。)
      • 是的,但这是一个例子。如果您担心编译没有标头,没有 shared_ptr 的命名空间,没有 foo 的声明...另一方面,这个概念是显而易见的。
      • 我同意帕特里克的观点。我立即注意到了它,但没有发表评论,因为这实际上只是一个例子:) 无论如何,我认为他的隐含问题应该由原始海报回答。我很好奇。
      • 基本上这个想法是禁止非托管堆或堆栈创建。但是,我想在文档中添加“做这个或死”评论也将实现这一点,并使我免于所有这些技巧。
      • 可以归类为过早的手术。有一天,有人可能有很好的理由对您的对象使用基本的 ptr。如果您在那一天之后必须支持他们的代码,那么您绝对有权杀死他们。
      【解决方案3】:

      似乎目标是让类的用户无法直接调用构造函数,而只公开一个返回shared_ptr的例程。

      但是如果你想应用这个模式,你需要在所有的子类中复制它。子类不能自动“继承” init(),因此 init() 仍然会调用子类的构造函数,因为 init() 不是虚拟方法,并且在没有对象的情况下调用。

      我会让构造函数像往常一样暴露,只使用标准

      shared_ptr<X> x = new X();
      

      这可以降低认知负担、可读性和灵活性。无论如何,这就是我们公司使用引用计数对象编程的方式。

      【讨论】:

        【解决方案4】:

        怎么样...

        template<typename Derived>
        class Foo 
        {
        public:
        
            static shared_ptr<Derived> init() 
            {
                return shared_ptr<Derived>(new Derived);
            }
        
            ~Foo() 
            {
            }
        
        protected:
        
            Foo()
            {
            }
        
        };
        
        
        class Bar : public Foo<Bar>
        {
        };
        
        int _tmain(int argc, _TCHAR* argv[])
        {
            shared_ptr<Bar> b = Foo<Bar>::init(); 
            return 0;
        }
        

        【讨论】:

          【解决方案5】:

          为什么不引入一个带有虚拟析构函数的公共基类,从它继承所有必要的类并简单地使用新的?

          【讨论】:

            【解决方案6】:

            通过隐藏构造函数来强制使用shared_ptr 创建对象通常不是一个好主意。我是根据个人经验与一个内部公司库一起工作的,这正是这样做的。如果你想确保人们总是包装他们分配的对象,只需确保存储这些类型实例的所有参数和成员都期望shared_ptrweak_ptr 而不是裸指针或引用。您可能还想从enable_shared_from_this 派生这些类,因为在共享所有对象的系统中,在某些时候您必须将this 指针传递给这些其他对象的方法之一,因为它们'重新设计为仅接受shared_ptr,如果您的对象没有internal_weak_this 以确保它不会被破坏,那么您的状态就很糟糕了。

            【讨论】:

            • 你知道,我从来不明白那东西是怎么工作的。在源代码中,他们没有分配给 _internal_weak_this 和明确的注释“不,你不必为它分配任何东西”。什么给了?
            • 查看 shared_ptr.hpp 源代码——特别是从 ctor(s) 调用的函数 sp_enable_shared_from_this 以及使用 sp_any_pointer dummy 的小技巧。
            【解决方案7】:

            整个层次结构的每个类型都需要静态工厂函数。

            class Foo
            {
            public:
                static shared_ptr< Foo > instantiate( /* potential arguments */ )
                {
                       return shared_ptr< Foo >( new Foo( /* potential arguments */ );
                }
            
            // blah blah blah
            };
            
            class Bar : public Foo
            {
            public:
                static shared_ptr< Bar > instantiate( /* potential arguments */ )
                {
                       return shared_ptr< Bar >( new Bar( /* potential arguments */ );
                }
            
            // blah blah blah
            };
            

            如果您仍有任何困惑,请在 sourceforge 上搜索 CppCodeProvider,看看它是如何完成的。

            【讨论】:

              【解决方案8】:

              顺便说一下,在大型 C++ 框架中,对编码器隐藏“自动内存管理”是很常见的。这让他可以编写更短更简单的代码。例如,在 Qt 中你可以这样做:

              QPixmap foo() {
                  QPixmap pixmap(10, 10);
                  return pixmap;
              }
              
              void bar() {
                  QPixmap a = foo(); // no copying occurs, internal refcount incremented.
                  QPixmap b = a;     // ditto.
                  QPainter p(&b);
                  p.drawPoint(5, 5); // data can no longer be shared, so a copy is made.
                  // at this point 'a' is still unchanged!
                  p.end();
              }
              

              与 Qt 中的许多东西一样,这模仿了 Java 对象模型,但通过实现 copy-on-write(它称为 implicit sharing)更进一步。这是为了让那些不习惯调用clone() 的 C++ 编码人员对 API 行为不那么惊讶。

              这是通过 d-pointer idiom 实现的,它用一块石头杀死两只鸟 - 您提供自动内存管理,并且您将您的实现与用户隔离 (pimpl)。

              你可以在这里查看QPixmap的实际实现:qpixmap.cppqpixmap.h

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2014-11-22
                • 1970-01-01
                • 2016-03-24
                • 2011-12-27
                • 2017-11-17
                • 2020-08-07
                • 2012-09-03
                相关资源
                最近更新 更多