【问题标题】:In C++, is it possible to reconcile stack-based memory management and polymorphism?在 C++ 中,是否可以协调基于堆栈的内存管理和多态性?
【发布时间】:2011-01-14 20:33:49
【问题描述】:

我喜欢在堆栈上声明变量,尤其是在使用标准容器时。每次避免new,就可以避免潜在的内存泄漏。

我也喜欢使用多态性,即带有虚函数的类层次结构。但是,这些功能似乎有点不兼容:你不能这样做:

std::vector<BaseType> vec;
vec.push_back(DerivedType())

或者至少看起来你会失去你推入的对象的多态性。

那么,有什么方法可以协调基于堆栈的内存管理和虚拟函数的使用?

【问题讨论】:

    标签: c++ oop memory-management stack


    【解决方案1】:

    您可以使用来自 Boost 库或 POCO C++ 库 http://www.pocoproject.org 的智能指针实现。

    include "Poco/SharedPtr.h"
    
    include "vector.h"
    
    class Base
    {
       protected:
          std::string somestring;
       public:
          Base()
          {
              somestring = "Hello";
          }
          virtual void Method1() { 
              std::cout << "Method1 " << somestring.c_str() << std::endl;
          }
          virtual ~Base()
          {
              std::cout << "Base" << std::endl;
          }
    };
    
    class Derived : public Base
    {
       public:
          void Method1() 
          { 
              std::cout << "overriden Method1 " << somestring.c_str() << std::endl;
          }
         ~Derived() 
         {
            std::cout << "Derived" << std::endl;
         }
    };
    
    int main()
    {
        std::vector<Poco::SharedPtr<Base> > someVector;
    
        for (int i = 0; i < 20 ; i++)
        {
            Poco::SharedPtr<Base> obj = new Base();
            Poco::SharedPtr<Derived> dObj = new Derived();
            someVector.push_back(obj);
            someVector.push_back(dObj);
        }
    
        std::vector<Poco::SharedPtr<Base> >::iterator itr = someVector.begin();
        for (int i = 0; i < someVector.size(); i++)
        {
            someVector[i]->Method1();   
        }
    
        return 0;
    }
    

    【讨论】:

      【解决方案2】:

      冲突不在多态性和(堆或堆栈)分配之间,因为标准容器通常在堆上分配它们的元素。

      冲突发生在多态性和(值或引用)语义之间。多态性需要引用语义带来的额外间接级别才能工作,但标准容器具有值语义。

      解决方案,正如其他答案中已经提到的,是存储其值是对其他对象的引用的对象,其中更简单的是指针。

      【讨论】:

        【解决方案3】:

        当您在堆栈上声明 std::vector 变量时,动态内存分配确实发生,用于向量的内部数组。它不一定来自堆。 std::vector 的第二个模板参数默认为std::allocator,您可以将其替换为您自己的自定义参数。

        对于管理多态类型,有boost::smart_ptrboost intrusive 库等工具。

        【讨论】:

          【解决方案4】:

          答案很明显:

          std::vector<BaseType*> vec;
          DerivedType d;
          vec.push_back(&d);
          

          但可能不是你想要的。 dvec 最好同时死掉;如果vecd 寿命长,那么你的指针不好。

          我认为你真正想要的是 Boost pointer containers:

          boost::ptr_vector<BaseType> vec;
          vec.push_back(new DerivedType());
          

          因此您不必担心泄漏。指针容器专门用于简化多态对象的使用和存储任务。

          【讨论】:

          • 您的第一个示例甚至无法编译,我看不出这是一个非真实代码示例;所以我希望你的意思是像 std::vector 这样的东西,一旦你将 vec 的副本传递给外部范围,它当然会失败......不过,指针容器 +1。
          • @Neil @gimpf:哎呀,是的。我只是复制了 OP 的 sn-p 时间,没有看到它“不正确”。
          【解决方案5】:

          智能指针是 cmets 建议的最佳解决方案。

          另一种技术——如果你主要使用多态性来隐藏你的类的实现细节——是Pimplidiom

          【讨论】:

            【解决方案6】:

            在这种情况下,您不仅失去了多态性,而且您将slice 派生类型对象转换为基类型对象。

            但您可以使用智能指针。

            std::vector<std::tr1::shared_ptr<BaseType> > vec;
            vec.push_back(std::tr1::shared_ptr<BaseType>(new DerivedType()));
            

            【讨论】:

              【解决方案7】:

              如果您的主要兴趣是防止内存泄漏,您可能希望查看智能指针。我会推荐Boost's Smart Pointers

              【讨论】:

              • 很好的一般建议,但是对于大型集合,智能指针会产生很大的开销;不过,这在大多数情况下是可以忽略的。
              猜你喜欢
              • 1970-01-01
              • 2020-10-10
              • 1970-01-01
              • 2011-01-28
              • 2017-04-28
              • 1970-01-01
              • 2021-06-28
              • 2011-11-28
              • 1970-01-01
              相关资源
              最近更新 更多