【问题标题】:Vector of Deep Copy of pointers指针的深拷贝向量
【发布时间】:2012-08-23 05:16:03
【问题描述】:

我是一个非常新的程序员和超级初学者,所以我对 c++ 了解不多。我特别有一个关于制作指针的深层副本的问题。我拥有的是一个充满 POD 的 A 类和一个指向该类 (A *P) 的指针。 我有第二个 B 类,其中包含一些其他 POD 和一个指向 A 类的指针向量。 我想填充 A *P 的深拷贝向量,因为在循环中我将动态分配和释放它。以下不起作用。我相信它是我的复制构造函数和 = 运算符的重载。这是我为了乐趣和学习而做的事情。

class A
{
   public:  
   .....
   .....
   .....
};

class B
{
   public:  
   B();
  ~B();
   B(const B &Copier);
   B& B::operator=(const B &Overloading);
   vector<A*> My_Container;
   A* Points_a_lot;
   int counter;
 };
B::B()
{
  counter=0;
  Points_a_lot=NULL;
}
B::~B()
{
   for(size_t i=0; i<My_Container.size(); i++)
   {
      delete My_Container[i];
    }
 }
 B::B(const B &Overloading)
 {
     My_Container[counter]=new A(*Overloading.Points_a_lot);
 }
 B& B::operator=(const B &Overloading)
 {
     if(!Overloading.My_Container.empty()) 
     {
         Overloading.My_Container[counter]=new B(*Overloading.Points_a_lot);
      }
      return *this; 
  } 
 int main()
 {  A* p=NULL;
    B Alphabet;
    for(....)
    {
        p=new A;
        //some stuff example p->Member_of_A=3; etc..
        Alphabet.My_Container[Alphabet.counter]=p;
        Alphabet.counter++;
       delete p;
     }
    return 0;
   }

任何帮助都会很棒。我感谢你的时间。假设包括所需的库。

【问题讨论】:

  • 忘记“深”或“浅”副本的概念。对于任何给定的数据结构,只有“正确”和“不正确”的副本。
  • hmm 你的意思是在这种情况下,以另一种方式复制会导致很多错误,因为指针正在被删除,所以真的只有一种方法吗?
  • 我必须说你的代码相当混乱,尤其是B 的重载 operator= 正在做一些不直观的事情的部分。此外,您的代码不会编译,因为Points_a_lotA*,而B 没有接受A* 的构造函数。

标签: c++ class pointers deep-copy


【解决方案1】:

好的,在我看来,您对 operator= 应该做什么感到非常困惑。在operator overloading 上查看此页面。这应该让您开始为该功能走上正确的道路。

其次,与您的问题无关,请查看this question,了解为什么您的字段(成员变量、what-have-you)应该是私有的。

【讨论】:

    【解决方案2】:

    您的代码中有很多错误。主要的是,您的赋值运算符和复制构造函数根本没有深度复制指向A 的指针的vector,而是试图将B* 放在向量的位置。您的赋值运算符应该做的是删除向量指向的元素,并在检查自赋值后用源对象向量指向的元素的深层副本填充它。您的复制构造函数应该填充源对象元素的深层副本。

    其次,您应该提供一种方法,将元素添加到您的类的向量中,并让它在内部设置计数器变量。必须在外部协调向量和计数器很容易出错,而 OOP 的优点之一是避免这种错误。但更好的是,完全删除 counter 变量。你不需要它。然后,您的 main 将被简化为:

    int main()
    {
      B Alphabet;
      for(....)
      {
        A* p = new A;
        //some stuff example p->Member_of_A=3; etc..
        Alphabet.appendElement(p); // B takes ownership, no need to delete in main
      }
    }
    

    appendElement 可能是

    class B
    {
     public:
      void appendElement(A* element) { myContainer_.push_back(element); }
      // other public methods
     private:
       std::vector<A*> myContainer_;
    };
    

    您可以通过存储某种单一所有权智能指针而不是原始指针来进一步改进所有这些。这意味着您不必担心自己进行删除。但这可能超出了这个问题的范围。

    现在,您应该考虑完全避免使用指针。在这种情况下,您必须不提供复制构造函数、赋值运算符或析构函数。编译器合成的就可以了。你的 B 类减少到

    class B
    {
     public:
      void appendElement(const A& element) { myContainer_.push_back(element); }
      // other public methods
     private:
       std::vector<A> myContainer_;
    };
    

    【讨论】:

    • @Managu 我添加了对自我分配的提及。但是有这么多基本错误,我不能指望让代码正常工作所需的所有内容。所以我不会说任何关于复制和交换的事情:-)
    【解决方案3】:

    我不完全了解您的要求,因此我尝试修复代码并深拷贝 B,因为这似乎是您的要求。

    #include <vector>
    using namespace std;
    
    class A
    {
    public:
        A() : m_someInt(0), m_someFloat(0.0f) {}
    
        // Redundant but putting it here for you to see when it is called (put break-point)
        A(const A& a_other)
        {
            m_someInt = a_other.m_someInt;
            m_someFloat = a_other.m_someFloat;
        }
    
        int   m_someInt;
        float m_someFloat;
    };
    
    class B
    {
    public:  
        B();
        ~B();
        B(const B &Copier);
        B& B::operator=(const B &Overloading);
        void Cleanup();
        void AddA(const A* a);
    
    private:
    
        vector<A*> My_Container;
        A* Points_a_lot;
    };
    
    B::B()
    {
        Points_a_lot=NULL;
    }
    
    B::~B()
    {
        Cleanup();
    }
    
    B::B(const B &Overloading)
    {
        // Deep copy B
        operator=(Overloading);
    }
    
    B& B::operator=(const B &Overloading)
    {
        // Delete old A's
        Cleanup();
    
        // Not using iterators to keep it simple for a beginner
        for (size_t i = 0; i < Overloading.My_Container.size(); ++i)
        {
            // We need new A's which will then copy from the A's in Overloading's container
            A* newA = new A( *(Overloading.My_Container[i]) );
            // Done with allocation and copy, push_back to our container
            My_Container.push_back(newA);
        }
    
        return *this; 
    }
    
    void B::Cleanup()
    {
        // Assuming B is not responsible for cleaning up Points_a_lot
        Points_a_lot = NULL;
    
        for (size_t i = 0; i < My_Container.size(); ++i)
        {
            delete My_Container[i];
        }
    
        // Automatically called when My_Container is destroyed, but here we 
        // are open to the possibiliy of Cleanup() being called by the client
        My_Container.clear(); 
    }
    
    void B::AddA(const A* a)
    {
        // We are adding a new A. In your code, the incoming A is going to 
        // be destroyed, therefore, we need to allocate a new A and copy 
        // the incoming A
        A* newA = new A(*a);
        My_Container.push_back(newA);
    }
    
    int main()
    {  
        A* p=NULL;
        B Alphabet;
        for(int i = 0; i < 10; ++i)
        {
            p = new A();
            //some stuff example p->Member_of_A=3; etc..
            Alphabet.AddA(p);
            delete p;
        }
    
        // If you put a breakpoint here and step through your code, you will see
        // `B` deep-copied
        B NewAlphabet = Alphabet;
    
        return 0;
    }
    

    几点说明:

    • Newing 和 deleteing A 在循环中是个坏主意。我意识到你这样做只是为了学习,这很好,但你可能要记住这一点。不要通过p 破坏A,而是允许B 获得新A 的所有权
    • 使用调试器单步调试代码以查看其工作原理
    • 在询问“为什么这不起作用/编译”时,尝试发布尽可能接近原始代码的代码

    【讨论】:

    • 我认为 OP 要求提供深层副本,所以我不确定是否有任何所有权可以转移到任何地方。
    • @juanchopanza:我明白你的意思。我假设 OP 正在询问如何正确复制包含指针的vector
    • 是的,除了没有“正确”,并且在要求“深拷贝”时,暗示每个B 拥有其向量中的指针指向的所有As .
    • @juanchopanza:你说的“没有‘适当的’”是什么意思?
    • Class B 可能正在存储其他人拥有的指针,在这种情况下,副本不需要很深。
    【解决方案4】:

    看起来你应该让你的 My_Container 由 unique_ptrs 组成,这样当你分配时,向量就会接管 A 实例的所有权

    for(....)
    {
        p=new A;
        //some stuff example p->Member_of_A=3; etc..
        Alphabet.My_Container[Alphabet.counter]=p;
        Alphabet.counter++;
       delete p;
     }
    

    所以改为将 My_Container 声明为

    vector<std::unique_ptr<A*> > My_Container;
    

    那么代码就是

    for(....)
    {
        p=new A;
        //some stuff example p->Member_of_A=3; etc..
        Alphabet.My_Container[Alphabet.counter]=p;
        Alphabet.counter++;
     }
    

    然后,如果您需要进行“深度复制”,请在 A 中创建一个名为 clone() 的成员,然后将 unique_ptr 返回给实例,在您需要创建副本时使用它。

    【讨论】:

    • 取得所有权的赋值会导致奇怪的语义。有点让我想起auto_ptr
    【解决方案5】:

    您可能想看看boost::ptr_vector。它的界面与std::vector 的界面非常相似,但它是为指针量身定制的,因此:

    • 拥有资源(因此没有内存泄漏)
    • 允许多态存储(派生类)
    • 是常量正确且复制正确的

    为了使副本生效,您必须提供 A* new_clone(A const*) 实现。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-28
      • 2015-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-09
      • 2012-07-06
      相关资源
      最近更新 更多