当存在要复制的值数组时,在C ++中重载=运算符

时间:2010-11-24 13:33:35

标签: c++ operators operator-overloading

我对C ++有点新鲜,所以我想这是一个非常基本的问题。

假设我有这门课程:

// file Graph.h
class Graph { 
public:
  Graph(int N); // contructor
  ~Graph();     // destructor  
  Graph& operator=(Graph other);
private:
  int * M;
  int N;
};

// file Graph.cpp
Graph :: Graph(int size) {
  M = new int [size];
  N = size;
}

Graph :: ~Graph() {
  delete [] M;
}

我想创建一个赋值运算符,它会复制数组M [的内容不会覆盖它当我在复制后更改它时(我认为这个通过不复制实际指针而只复制内容来完成,不知道我是否正确。这就是我尝试过的:

Graph& Graph::operator=(Graph other) {
  int i;
  N = other.N;
  M = new int [N];
  for (i = 0; i < N; i++)
    M[i] = other.M[i];
  return *this;
 }

这是对的吗?还有其他方法吗?

编辑:我忘记了一个重要问题。为什么我必须将其声明为Graph& operator=(Graph other);,而不仅仅是:Graph operator=(Graph other);这是我书中所写的内容(C ++:The Complete Reference,2nd ed,Herbert Schildt,355-357页)?

8 个答案:

答案 0 :(得分:9)

规范的方法是使用std::vector<int>来避免自己管理内存。但是,对于练习来说,做正题的正确方法是:

#include <algorithm>

class Graph
{
public:    
    Graph(size_t n) { data_ = new int[n](); size_ = n; }

    Graph(Graph const& g)
    {
        data_ = new int(g.size_);
        size_ = g.size_;
        std::copy(g.data_, g.data_ + g.size_, data_);
    }

    ~Graph() { delete[] data_; }

    void swap(Graph& g) throw()
    {
        std::swap(data_, g.data_);
        std::swap(size_, g.size_);
    }

    Graph& operator=(Graph g) { g.swap(*this); return *this; }

private:
    int* data_;
    size_t size_;
};

Google“复制和交换习惯用法”代码背后的理由。请注意,您的分配运算符会泄漏内存(原始数组会被覆盖,但永远不会被删除),如果分配失败,最终会导致对象损坏。而且,x = x不会做预期的事情。这三个陷阱很常见,以复制交换方式编写赋值运算符可以避免它们。

编辑:对于您的其他问题,返回引用允许您链接分配,如a = b = c,这对内置类型有效。它可能是也可能不是你想要的(通常是)。

答案 1 :(得分:3)

您需要&中的operator=,以便在您返回*this时不会导致副本。更重要的问题是为什么你需要退货。答案是支持a=b=c语法。

我建议你使用memcpy作为内置类int(或指针)的标准数组。该标准以一种方式定义它,使编译器编写者能够提供尽可能快的,特定于平台的实现。

如果类型是对象(不会调用复制构造函数和许多其他坏东西),请不要使用memcpy。

答案 2 :(得分:1)

您可能想要声明并定义复制构造函数。

事实上,在您的情况下这是必须的,因为默认的复制构造函数在销毁期间将导致双delete

我认为在赋值运算符中使用复制构造函数更为惯用(复制构造后跟交换)。

对于当前代码,存在内存泄漏(因为旧的M不是delete d)。

答案 3 :(得分:1)

几乎所有人都说过,但有一些重要的注意事项:

  • 建议将参数传递给复制构造函数和赋值运算符作为 const引用,即const Graph& other,以避免大量复制到临时对象(除非您使用{{ 3}}
  • 如果您使用指针作为类成员,必须(好吧,在大多数情况下应该)同时提供copy ctor和赋值运算符,或者通过将它们设为私有来禁用它们,否则默认值将导致泄漏。
  • 最后,为什么不使用std::vector?将为您节省所有这些麻烦而不会有明显的性能损失。

此页面可能会有所帮助 - 简单而全面:"copy and swap" idiom

答案 4 :(得分:1)

其他人已经说过其中一些,但如果我们想坚持你所拥有的基本布局,你应该改变这个:

  • 使“Graph other”不变 - &gt; “const Graph other”(很高兴,所以你不会'意外'改变“其他”中的数据)
  • 检查“其他”以查看它与您指定的对象(Lvalue)不是同一个对象。可以通过制作一个简单的if语句来做到这一点 - &gt; if(this ==&amp; other)
  • 删除M中的内存。(我们不希望内存泄漏:))
哦顺便问一下。您不必通过在函数开头声明“i”来使用C语法 - &gt; “for(int i = ....”

希望它有所帮助:)

答案 5 :(得分:0)

你也可以在这里使用std :: copy std::copy

或者你也可以memcpy数组

答案 6 :(得分:0)

不要忘记写graph_a = graph_a是合法的;您的代码将泄漏最初为graph_a.M分配的内存,并且在复制后您将在M中获得未初始化的内存。

在进行复制分配之前,您必须检查您是否未将相同的对象复制到自身上(在这种情况下,您可以返回)。

答案 7 :(得分:0)

您必须返回对已构造对象的引用。如果您返回了副本,则您将继续复制构造另一个对象,丢弃您已有的对象。