4

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

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

4

5 回答 5

3

好的,在我看来,您对 operator= 应该做什么感到非常困惑。看看这个关于操作符重载的页面。这应该让您开始为该功能走上正确的道路。

其次,与您的问题无关,请查看有关为什么您的字段(成员变量、您拥有什么)应该是私有的这个问题。

于 2012-08-23T05:31:33.163 回答
2

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

其次,您应该提供一种将元素添加到类向量的方法,并让它在内部设置计数器变量。必须在外部协调向量和计数器是容易出错的,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_;
};
于 2012-08-23T05:30:06.430 回答
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;
}

几点注意事项:

  • New循环中的ing 和deleteingA是一个坏主意。我意识到你这样做只是为了学习,这很好,但你可能要记住这一点。与其破坏Ap不如让B自己拥有新的东西A
  • 使用调试器单步执行代码以查看其工作原理
  • 当询问“为什么这不起作用/编译”时,尝试发布尽可能接近原始代码的代码
于 2012-08-23T05:57:48.223 回答
0

看起来你应该让你的 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() 的成员并将 a 返回unique_ptr给实例,在需要创建副本时使用它。

于 2012-08-23T05:47:59.053 回答
0

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

  • 拥有资源(所以没有内存泄漏)
  • 允许多态存储(派生类)
  • 是 const 正确且复制正确的

为了使副本起作用,您必须提供一个A* new_clone(A const*)实现。

于 2012-08-23T06:40:43.343 回答