【问题标题】:fixing shallow copy with a copy constructor使用复制构造函数修复浅拷贝
【发布时间】:2020-06-08 23:08:48
【问题描述】:

我定义了一个复制构造函数来避免数据成员 n 的浅拷贝,但它不起作用,当我更改 v1 时 v2 仍然在更改,我做错了什么?

#include<iostream>
using namespace std;
class Vector
{
  public:
  int *n;

  Vector();
  Vector(const Vector& vector );

};
Vector::Vector()
{
  cout<<"Constructor called"<<endl;
}
Vector::Vector(const Vector& vector )
{
  n=new int;
  n=vector.n;
  cout<<"Copy constructor called"<<endl;
}



int main()
{
  Vector v1;
  int x=5;
  v1.n=&x;
  Vector v2=v1;

  cout <<"Vector v1 has n value: "<<*v1.n<<endl;
  cout <<"Vector v2 has n value: "<<*v2.n<<endl;
  *v1.n=499; 
  cout <<"Vector v1 has n value: "<<*v1.n<<endl;
  cout <<"Vector v2 has n value: "<<*v2.n<<endl;

  return 0;
}


【问题讨论】:

  • 您有内存泄漏。存储在v1.n 中的地址正在保存到v2.n。使用new int 分配的内存丢失。所以他们现在指向同一个内存。如果你只想复制整数值,你需要做*n = *vector.n。但是你不是在做一个浅拷贝。你正在做一个深拷贝。
  • 您需要正确实施它。您分配 n,然后通过复制指针泄漏内存,执行默认副本的操作。您应该将 vector.n 的内容复制到您分配的内容中。例如,像这样:*n = *vector.n。如果您实现了复制构造函数,您还应该实现 = 运算符。
  • ^^^ 和析构函数。如果您感觉很奇怪,请添加移动分配和移动构造函数。
  • 我应该分配内存吗?我不确定是否必须输入 'n=new int;'
  • 还阅读了“三规则”或(C++ 11 及更高版本)五规则。简而言之,三法则是,如果你的类需要手动定义复制构造函数、赋值运算符或析构函数之一,则所有这些都需要定义。五规则将其扩展到包括移动构造和移动分配。

标签: c++ oop copy-constructor shallow-copy


【解决方案1】:

问题是您的复制构造函数实现错误。您尝试修复浅拷贝问题,但您的代码包含一个语句,重新引入同样的问题。

在这一行:

n=new int;

你正确分配了一个新的int,这很好。

但是在这一行:

n=vector.n;

您正在丢弃刚刚分配的int,而是将n 指向另一个向量的n 指向的相同int。这又是浅拷贝问题。

要正确解决问题,您需要将另一个向量的n 成员的值复制到您新分配的n 成员中,例如:

n = new int;
*n = *(vector.n);

或者:

n = new int( *(vector.n) );

话虽如此,要完全遵守"Rule of 3",您还需要为释放n 添加一个析构函数(这意味着您不能使v1 指向本地int 变量在main()),以及添加一个复制赋值运算符来复制n(类似于复制构造函数)。在 C++11 中,您还可以通过添加移动构造函数和移动赋值运算符来选择遵守“5 规则”。

试试这个:

#include <iostream>
#include <utility>
//using namespace std; // <-- bad practice!

class Vector
{
private:
  int *m_n;

public:
  Vector(int x = 0);
  Vector(const Vector &src);
  Vector(Vector &&src);
  ~Vector();

  Vector& operator=(const Vector &src);
  Vector& operator=(Vector &&src);
  // alternatively, both operators can be merged into one:
  // Vector& operator=(Vector src);

  // you should not give outside code directly access to the int* itself,
  // that would promote bad practices, and potentially crash your class's
  // internal code if outside code assigns a bad pointer.
  //
  // these accessors provide safer use of the *value* that the int* points
  // at, let the class itself be the sole handler of the int* itself ...
  int n() const;
  void n(int x);
};

Vector::Vector(int x)
  : m_n( new int(x) )
{
  std::cout << "Converting Constructor called" << std::endl;
}

Vector::Vector(const Vector &src)
  : m_n( new int(src.n()) )
{
  std::cout << "Copy constructor called" << std::endl;
}

Vector::Vector(Vector &&src)
  : m_n( src.m_n )
{
  src.m_n = nullptr;
  std::cout << "Move constructor called" << std::endl;
}

Vector::~Vector()
{
  delete m_n;
  std::cout << "Destructor called" << std::endl;
}

Vector& Vector::operator=(const Vector &src)
{
  if (&src != this) {
    *m_n = src.n();
  }
  std::cout << "Copy assignment called" << std::endl;
  return *this;
}

Vector& Vector::operator=(Vector &&src)
{
  Vector tmp(std::move(src));
  std::swap(m_n, tmp.m_n);
  std::cout << "Move assignment called" << std::endl;
  return *this;
}

/* alternatively:
Vector& Vector::operator=(Vector src)
{
  std::swap(m_n, src.m_n);
  std::cout << "Assignment called" << std::endl;
  return *this;
}
*/

int Vector::n() const
{
  return *m_n;
}

void Vector::n(int x)
{
  *m_n = x;
}


int main()
{
  Vector v1(5);
  Vector v2 = v1;

  std::cout << "Vector v1 has n value: "<< v1.n() << std::endl;
  std::cout << "Vector v2 has n value: "<< v2.n() << std::endl;

  v1.n(499);

  std::cout << "Vector v1 has n value: " << v1.n() << std::endl;
  std::cout << "Vector v2 has n value: "<< v2.n() << std::endl;

  return 0;
}

【讨论】:

  • 你能解释一下 operator= 部分在做什么吗?
  • 复制构造函数正在做同样的事情,但是在一个已经存在的Vector对象上。例如:Vector v2 = v1; 是复制构造,而Vector v2; v2 = v1; 是复制分配。实际上,在内部使用复制构造函数实现复制赋值运算符是很常见的(参见copy-swap idiom
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-09
  • 1970-01-01
  • 2021-11-22
  • 2013-03-19
  • 1970-01-01
相关资源
最近更新 更多