试图找出课程类的复制构造函数

Trying to figure out a Copy Constructor for the course class

提问人:TobyFromHR 提问时间:8/30/2023 更新时间:9/5/2023 访问量:75

问:

这是有问题的课程类。

class Course {
    //** You may not change the declarations in this private: area.
    CourseName name;           ///< Name of the course
    int numberOfPrerequisites; ///< How many prereqs are currently in the array?
    int maxPrerequisites;      ///< How many prereqs will fit into the array?
    CourseName * prereqs;  

这是我目前所拥有的。请注意,此复制构造函数会导致我的一些单元测试失败。我无法更改单元测试,而是必须修复复制构造函数:

Course::Course(const Course& other)
    : name(other.name), numberOfPrerequisites(other.numberOfPrerequisites),
      maxPrerequisites(other.maxPrerequisites), prereqs(new CourseName[other.maxPrerequisites])
{
    // Copy all prerequisites
    for (int i = 0; i < numberOfPrerequisites; ++i)
    {
        prereqs[i] = other.prereqs[i];
    }
}

失败的单元测试表示,一旦使用另一门课程初始化了新课程,当您修改新创建的课程时,它也会更改原始课程。简单地说,它们在修改后是相同的,而它们应该是不同的。

这些是我迄今为止尝试过的运算符重载。

bool Course::operator==(const Course& other) const
{
    return name == other.name;
}
Course& Course::operator=(const Course& other)
{
    if (this == &other)
        return *this;

    name = other.name;
    numberOfPrerequisites = other.numberOfPrerequisites;
    maxPrerequisites = other.maxPrerequisites;

    delete[] prereqs;

    prereqs = new CourseName[other.maxPrerequisites];

    // Copy the prerequisites from the source object to the new object
    for (int i = 0; i < numberOfPrerequisites; ++i)
    {
        prereqs[i] = other.prereqs[i];
    }

    return *this;
}
C++ 运算符重载 copy-constructor

评论

1赞 user4581301 8/30/2023
建议:确保正确遵守 3/5/0 规则,并替换为 0 规则,并且可以遵守零规则。然后你就会得到什么都不做的乐趣!一般来说,在堆栈的下方,您可以放置任何专门的复制逻辑越好,因为上面的所有内容都是愚蠢的零法则。CourseNameCourseName * prereqs;std::vector<CourseName> prereqs;Course
0赞 user4581301 8/30/2023
啊。我读得更深一些。明确要求你像白痴一样编写代码。我对你有感觉。
1赞 user4581301 8/30/2023
但是,您可以通过 Copy 和 Swap 实现赋值运算符。这总是一个好的起点。它可能不是最佳性能,但几乎不可能出错,测试会让你知道它是否太慢。
0赞 user4581301 8/30/2023
假设符合 3/5/0 规则,我看不出该复制构造函数有任何问题。我认为我们需要看到一个最小的可重复示例(假设制作示例不会以您发现并修复错误而提前结束并且问题变得无用(在这种情况下考虑自我回答以尝试挽救问题))。CourseName
1赞 tbxfreeware 8/30/2023
一个好的经验法则是,相等应比较在复制 ctor 中复制的相同成员。这样,如果您制作副本,副本将与原件相同。但是,该规则也有例外。例如,为了相等,类的两个实例应具有相同的名称和相同的先决条件列表。但是,他们可能不需要具有相同的 .这些考虑表明,问题中给出的平等性是有缺陷的。operator==CoursemaxPrerequisitesoperator==

答:

0赞 tbxfreeware 8/30/2023 #1

这似乎是某种家庭作业,所以我不想在这个答案中透露太多。

也就是说,评论已经确定了这些事情:

  1. copy-ctor 似乎没问题。
  2. 复制分配也是如此。operator=
  3. dtor 丢失。
  4. 平等是不足的。operator==

缺少 dtor 将导致内存泄漏。但是,缺少它不应导致复制 ctor 或复制分配失败。operator=

失败的单元测试表明,一旦使用另一门课程初始化了新课程,当您修改新创建的课程时,它也会更改原始课程。

如果 copy-ctor 正确地完成了它的工作——这似乎是这种情况——那么唯一可能出错的就是复制 .CourseName

起初,我以为它有一个简单的定义,如下所示:

struct CourseName
{
    std::string name;
    bool operator== (CourseName const&) const = default;
};

但如果这是真的,那么复制器就不会有错误。

就在那时,我突然想到这是一堂关于深度复制和深度比较的课程。教师是否实施了“三法则”?CourseName

struct CourseName
{
    char const* name_{ nullptr };
public:
    CourseName(char const* name);

    // Rule of Three
    ~CourseName();
    CourseName(CourseName const& other);
    CourseName& operator=(CourseName const& other);

    // Other member functions and operators
    bool operator== (CourseName const& other) const;
};

如果是这样,那么编译器提供的默认复制分配将不做正确的事情。进行成员比较的平等也不会。operator=CourseNameoperator==

因此,你的问题可能是你需要做更多的工作。CourseName

如果不是,那么我们将需要看到一个最小的、可重复的例子,以便进一步分析。那应该是一个完整的程序,包括功能。MRE 会排除不必要的细节,但会重现错误。main

什么是“强力保证”?

所谓“强保证”,就是当OR等算子失败时,原物不变的承诺。这是全有或全无:不允许部分更新。operator=operator>>

原问题中给出的并不能提供强有力的保证。对 operator 的调用可能会失败,抛出 .发生这种情况时,您将无法恢复原始对象,因为您已经将其删除。operator=new[]std::bad_alloc

为了提供强有力的保证,您需要将运算符的结果分配给临时变量,并且仅在安全接收到该指针才更新对象。new[]

这是你的,只需要很少的改变来提供强有力的保证。operator=

Course& Course::operator=(const Course& other)
{
    if (this == &other)
        return *this;

    // If the call to operator new[] is going to fail, 
    // we need that to happen before we start deleting 
    // the original object. That is why we do this first.
    auto temp = new CourseName[other.maxPrerequisites];

    // Operator new[] did not throw std::bad_alloc.
    // Now it is safe to start overwriting things.
    name = other.name;
    numberOfPrerequisites = other.numberOfPrerequisites;
    maxPrerequisites = other.maxPrerequisites;

    delete[] prereqs;

    // Assign the temporary pointer created above.
    prereqs = temp;

    // Copy the prerequisites from the source object to the new object
    for (int i = 0; i < numberOfPrerequisites; ++i)
    {
        prereqs[i] = other.prereqs[i];
    }

    return *this;
}

如评论中所述,复制和交换习语是实现 .复制和交换具有简单性的好处,此外,还提供了强有力的保证。operator=

评论

0赞 TobyFromHR 8/30/2023
我实现了一个析构函数,我只是未能在原始问题中显示它,我很抱歉。courseName 类已经满足了 3 法则,并且该代码需要保持不变。我对 operator== 的问题是我无法弄清楚如何编写它,以便它检查相同的 courseName 以及相同的 prereq 数组。
0赞 tbxfreeware 8/31/2023
首先与 .如果它们相同,请运行循环。在循环中,使用 if 语句来比较相应的元素。为了更加谨慎,您甚至可以在运行循环进行比较之前对数组进行排序。this->numberOfPrerequisitesother.numberOfPrerequisitesfor (std::size_t i{}; < numberOfPrerequisites; ++i) ...CourseName
0赞 tbxfreeware 8/31/2023
除非我遗漏了什么,否则我看不出修复将如何修复您在原始问题中描述的错误:“当您修改新创建的课程时,它也会更改原始课程。这就是使用浅拷贝而不是深拷贝时看到的症状。operator==
0赞 tbxfreeware 8/31/2023
我刚刚又看了一眼你的 copy-ctor 和 copy-assignment 运算符。再一次,我开始怀疑这是罪魁祸首。你能编辑原始问题,并发布完整的源代码吗?CourseNameCourseName