指针的深拷贝传染媒介

时间:2012-08-23 05:16:03

标签: c++ class pointers deep-copy

我是一个非常新的程序员和超级初学者,所以我对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;
   }

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

5 个答案:

答案 0 :(得分:3)

好吧,在我看来,你对操作员应该做什么非常困惑。请查看operator overloading上的此页面。这应该可以帮助您开始使用该功能的正确途径。

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

答案 1 :(得分:2)

您的代码中存在许多错误。主要的一点是你的赋值运算符和复制构造函数根本没有将vector的指针深度复制到A,而是试图将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_;
};

答案 2 :(得分:1)

我并不完全明白您的要求是什么,因此我尝试修复代码并执行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;
}

一些注意事项:

    循环中的
  • Newdelete A是一个坏主意。我知道你这样做只是为了学习,这很好,但你可能想要牢记这一点。不要通过A销毁p,而是允许B取得新A
  • 的所有权
  • 使用调试程序逐步查看代码,了解其工作原理
  • 在询问“为什么不工作/编译”
  • 时,尝试发布尽可能接近原始代码的代码

答案 3 :(得分:0)

看起来你应该让你的My_Container由unique_ptrs组成,这样当你分配时,vector会接管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返回给实例,当你需要创建一个副本时使用它。

答案 4 :(得分:0)

您可能需要查看boost::ptr_vector。它的接口与std::vector的接口非常相似,但它是针对指针而定制的,因此:

  • 拥有资源(所以没有内存泄漏)
  • 允许多态存储(派生类)
  • 是const-correct和copy-correct

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