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

发布于 2024-10-04 00:52:22 字数 900 浏览 6 评论 0原文

我对 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,第二版,Herbert Schildt,第 355-357 页)?

I'm somewhat new to C++ so, I guess this is a very basic question.

Suppose I have this class:

// 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;
}

I want to create an assignment operator that will copy the contents of array M[] but not to overwrite it when I change it after the copy (I think this is accomplished by not copying the actual pointer but only the content, don't know if I'm right). This is what I've tried:

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;
 }

Is this correct? Are there other ways to do this?

edit: An important question I forgot. Why I must declare it like Graph& operator=(Graph other); and not just: Graph operator=(Graph other); which is what's written in my book (C++: The Complete Reference, 2nd ed, Herbert Schildt, pages 355-357)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

╰つ倒转 2024-10-11 00:52:22

规范的方法是使用 std::vector 来避免自己管理内存。不过,对于这个练习,做你想做的事情的正确方法是:

#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_;
};

谷歌“复制和交换习惯用法”来了解代码背后的基本原理。请注意,您的分配运算符会泄漏内存(原始数组被覆盖但从未删除),如果分配失败,您最终会得到一个损坏的对象。此外,x = x 不会执行预期的操作。这三个陷阱很常见,以复制和交换方式编写赋值运算符可以避免它们。

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

The canonical way is to use a std::vector<int> to avoid managing memory yourself. For the exercise though, the right way to do what you want is:

#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 "copy and swap idiom" for the rationale behind the code. Note that your assigment operator leaks memory (the original array is overwritten but never deleted) and if the allocation fails, you end up with a broken object. Moreover, x = x won't do what it is expected to do. These three pitfalls are common, and writing assignment operators in copy-and-swap style avoids them.

EDIT: For your other question, returning a reference allows you to chain assignments, like a = b = c, which is valid for built in types. It may or may not be what you want (it usually is).

胡大本事 2024-10-11 00:52:22

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

我建议您将 memcpy 用于内置 int 类型(或指针)的标准数组。该标准的定义方式为编译器编写者提供了一种提供最快的、特定于平台的实现的方法。

如果类型是对象,请勿使用 memcpy (不会调用复制构造函数和许多其他不好的事情)

You need the & in operator= so that it doesn't cause a copy when you return *this. The more important question is why do you need to return anything. The answer is to support a=b=c syntax.

I would suggest you use memcpy for standard arrays of built-in int-like types (or pointers). The standard defines it in a way that gives the compiler writer a way to provide the fastest possible, platform-specific implementation.

Do not use memcpy if the type is an object (won't call copy constructor and a lot of other bad things)

慵挽 2024-10-11 00:52:22

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

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

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

至于当前代码,存在内存泄漏(因为旧的M没有deleted)。

You'd probably want to declare and define a copy constructor.

In fact, it is a must in your situation because the default copy constructor will result in a double delete during destruction.

I think it's more idiomatic to use the copy constructor in the assignment operator (a copy construction followed by a swap).

As for the current code, there is a memory leak (because the old M is not deleted).

浅暮の光 2024-10-11 00:52:22

其中一些已经被其他人说过,但是如果我们想坚持使用您拥有的基本布局,您应该更改此设置:

  • 使“Graph other”常量 -> “const Graph other”(很好,这样您就不会“意外”更改“other”中的数据)
  • 检查“other”以确保它与您分配给的对象不是同一个对象(左值)。可以通过一个简单的 if 语句来做到这一点 -> if(this == &other)
  • 删除 M 中的内存。(我们不希望内存泄漏:) )

哦顺便说一句。您不必通过在函数开头声明“i”来使用 C 语法 -> “for(int i =....”

希望有帮助:)

Some of it have already been said by others, but if we want to stick with basic layout that you have, you should change this:

  • Make "Graph other" constant -> "const Graph other" (Good to do, so you don´t 'accidentally' change the data in "other")
  • Have a check on "other" to see that it is not the same object as the object you are assigning to(the Lvalue). Can do this by making a simple if statement -> if(this == &other)
  • Delete the memory in M. (We don want memory leaks :) )

Oh btw. You don´t have to use C syntax by declaring "i" in the start of the function -> "for(int i =...."

Hope it helped :)

满天都是小星星 2024-10-11 00:52:22

差不多都说了,但是有几个重要的注意事项:

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

这个页面可能会有所帮助。全面:C++ 运算符重载指南

almost all have been said, but there are a few important notes:

  • it is advised to pass the parameter to copy constructor and assignment operator as const reference, i.e. const Graph& other, to avoid heavy copying to a temp object (unless you are using a "copy and swap" idiom
  • if you use pointer as a class member you must (well, should in most cases) provide both copy ctor and assignment operator, or disable them by making them private, otherwise the default ones will cause a leak.
  • finally, why not use std::vector? will save you all this trouble without noticeable performance penalty.

this page may be helpful - simple and comprehensive: C++ Operator Overloading Guidelines

顾冷 2024-10-11 00:52:22

您还可以在此处使用 std::copy 更多 std::copy

或者您可以< code>memcpy 数组也是如此

You could also use std::copy more here std::copy

or you can memcpy arrays as well

笑饮青盏花 2024-10-11 00:52:22

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

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

Don't forget that it's legal to write graph_a = graph_a; your code will leak the memory originally allocated for graph_a.M, and you'll end up with uninitialised memory in M after the copy.

Before doing a copy-assignment, you must check that you're not copying the same object over itself (in which case you can just return).

脸赞 2024-10-11 00:52:22

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

You must return a reference to the object that you have constructed. If you returned a copy, you would then proceed to copy construct another object, discarding the one that you already have.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文