正确地重载具有指针成员的类的赋值运算符

时间:2016-09-08 06:28:43

标签: c++ pointers overloading

如果我错了,请纠正我:

我理解当有一个带有成员指针的类时,类对象的副本将导致表示相同内存地址的指针。这可能导致对一个类对象进行更改以影响此对象的所有副本。

解决此问题的方法可能是重载 = 运算符。鉴于下面的示例,尝试创建动态数组类,为什么对 MyArray1 进行更改会更改 MyArray2

数组类:

#include <iostream>
#include <cstdlib>

class Array
{
public:
    Array(int N){               //constructor sets size of array
         size = N;
         arr = new int[N];
    }

    ~Array();

    int size;    //array elements
    int *arr;    //dynamic array pointer

    //fill array with random values between 1 and 100
    void fillArray() {
         for (size_t i = 0; i < size; i++)
              {arr[i] = std::rand()%100;}
    }

    //print out array to console
    void printArray() {
         for (size_t i = 0; i < size; i++)
             { std::cout << arr[i] << " ";}
             std::cout << std::endl;
    }

    //overload = operator
    Array &operator=(Array arr2) {
        std::swap(size, arr2.size);
        std::swap(arr, arr2.arr);
        return *this;
    }
};

Main.cpp的:

 #include "Array.h"
 #include <iostream>

int main(){
    Array MyArray1(8), MyArray2(8);

    MyArray1.fillArray();
    MyArray2 = MyArray1;

    std::cout << "Print out arrays:" << std::endl;
    std::cout << "MyArray1: "; MyArray1.printArray();
    std::cout << "MyArray2: "; MyArray2.printArray();
    std::cout << std::endl;

    MyArray1.arr[5] = 1000;
    std::cout << "MyArray2: "; MyArray2.printArray();

    MyArray1.fillArray();
    std::cout << "MyArray2: "; MyArray2.printArray();    

    return 0;
}

示例输出:

Print out arrays:
MyArray1: 41 67 34 0 69 24 78 58
MyArray2: 41 67 34 0 69 24 78 58

MyArray2: 41 67 34 0 69 1000 78 58
MyArray2: 62 64 5 45 81 27 61 91

如上所示,对MyArray1所做的更改会更改MyArray2。我认为 = 的重载是错误的,但我怎么写得正确呢?

SOLUTION:

感谢Chris Dodd的评论,我意识到这只是在我的班级中实现这样的复制构造函数:

Array(const Array &arr2){
        size = arr2.size;
        arr = new int[size];

        for (size_t i = 0; i < size; i++)
        {
            arr[i] = arr2.arr[i];
        }
}

2 个答案:

答案 0 :(得分:5)

您的代码存在多个问题。最重要的是,正如对问题的评论所指出的,除了编写自己的赋值运算符之外,还需要编写复制构造函数(并实现析构函数)。

第二件事是,你的赋值运算符通过值而不是引用来获取参数,这会导致默认的复制构造函数在传递给赋值运算符之前创建MyArray1的副本。这就是问题的直接来源所在。

另一件事是你的赋值运算符的行为类似于移动赋值而不是复制赋值,这意味着它将原始项的值替换为其当前(默认)值。

最后,你真的想要实现析构函数,这样它就会删除你的数组而不是泄漏它。

总结一下,你想要这样的东西(编辑:受Timothy答案启发的复制作业检查):

class Array
{
public:
    Array(int N)
    {
         size = N;
         arr = new int[N];
    }

    //destructor
    ~Array()
    {
        delete[] arr;
    }

    //copy constructor
    Array(const Array& arr2)
    {
        size = arr2.size;
        arr = new int[size];
        std::memcpy(arr, arr2.arr, size);
    }

    //overload = operator
    Array& operator=(const Array& arr2) 
    {
        if (this == &arr2)
            return *this; //self assignment
        if (arr != NULL)
            delete[] arr; //clean up already allocated memory

        size = arr2.size;
        arr = new int[size];
        std::memcpy(arr, arr2.arr, size);
        return *this;
    }

private:
    int size;    //array elements
    int *arr;    //dynamic array pointer
};

答案 1 :(得分:1)

所以,我看到了三个问题。

  1. 你应该有一个复制构造函数。你永远不知道你的编译器可能做了什么优化(改变了对copy-constructor的赋值等等)。

  2. 分配时应检查自我分配。在执行任务时,您希望删除旧数据并将其替换为新数据。 如果您进行自我分配,您将遇到错误(在您重新分配给自己之前删除数据)以及您不需要做的额外工作。

  3. 调用析构函数时,不处理动态分配的数据。这将导致内存泄漏,对于长时间运行的程序来说,这将成为一个大问题。

  4. 代码更新:

    ~Array(){
      if (arr != NULL)
        delete arr;
    }
    
    Array(const Array& arr2){
      size = arr2.size;
      arr = new int[Size];
      for (int i = 0; i < n; ++i){ //Copy each array element into new array
        arr[i] = arr2.arr[i];
      }
    }
    
    Array& operator= (const Array& arr2){
      if (this == &arr2){ return *this; } //make sure you aren't self-assigning
      if (arr != NULL){ delete arr; } // get rid of the old data
      //same as copy constructor from here
      size = arr2.size;
      arr = new int[size];
      for (int i = 0; i < n; ++i){
        arr[i] = arr2.arr[i];
      }
      return *this;
    }