2

我有一个类(那些读过 Accelerated C++ 的人可能会觉得这个类很熟悉)定义如下:

class Student_info{
public:
    Student_info() : midterm(0.0), final(0.0) {};
    Student_info(std::istream& is){read(is);};

    Student_info(const Student_info& s);

    ~Student_info();

    Student_info& operator=(const Student_info& s);

    //Getters, setters, and other member functions ommited for brevity

    static int assignCount;
    static int copyCount;
    static int destroyCount;

private:
    std::string name;
    double midterm;
    double final;
    double finalGrade;
    std::vector<double> homework;

};

typedef std::vector<Student_info> stuContainer;


bool compare(const Student_info& x, const Student_info& y);

函数calculator() 使用这种类型的对象。作为函数的一部分,使用库的通用排序函数对(已声明的)Student_info 对象的向量进行排序。我的程序没有超过这一点(尽管根据 NetBeans 没有抛出异常并且程序正确退出)。

sort 函数大量使用容器中保存的任何类型的赋值运算符,但我似乎无法找出我定义的那个有什么问题(程序在我定义之前运行正常)。根据Accelerated C++(或者至少我是这样解释的),赋值运算符应该工作的正确方式是首先破坏左操作数,然后用等于右操作数的值再次构造它。所以这是我重载的 operator= 定义:

Student_info& Student_info::operator=(const Student_info& s)
{
    if(this != &s)
    {
        this->~Student_info();
        destroyCount++;

        *this = s;
    }

    return *this;
}

如您所见,它调用了 Student_info 复制构造函数,定义如下:

Student_info::Student_info(const Student_info& s)
{
    name = s.name;
    midterm = s.midterm;
    final = s.final;
    finalGrade = s.finalGrade;
    homework = s.homework;

    copyCount++;
}

复制构造函数正确运行,因为省略排序语句允许程序正确运行并产生大于 0 的 copyCount(仅在复制构造函数和 operator= 中修改)。

那么我的赋值运算符到底有什么问题?它与调用 Student_info 对象的破坏有关,但我不知道如何纠正它而不是破坏它。

(顺便说一句,Accelerated C++ 中的一个练习要求创建复制构造函数、析构函数和赋值运算符……我意识到这些函数的合成版本显然足以满足我的课程)

4

3 回答 3

6

不不不。它根本不应该那样工作。您当前的赋值运算符破坏了它被调用的对象,然后在被破坏的对象上调用自身(哦,嘿,无限递归)(哦,嘿,未定义的行为)。你不应该破坏现有的对象。完全没有。而且这段代码*this = s根本不调用任何构造函数,它调用赋值运算符——这就是你刚刚定义的。复制构造函数调用看起来像new (this) Student_info(s);. 这是一种已知的模式,它在很多方面都很糟糕。如果你有一本书推荐它,把它扔进垃圾箱

赋值运算符应该将数据从右侧复制到左侧。在大多数情况下,最简单的方法就是复制每个数据成员。这个操作符的语义不涉及破坏任何东西。使用此运算符的任何人都有权期望不会破坏任何 Student_info 对象。

只需调用成员现有的赋值运算符,然后实现您需要的任何其他逻辑。

于 2011-01-26T16:19:14.203 回答
2
*this = s;

这是无尽的递归。它不是复制构造函数,它是赋值运算符

于 2011-01-26T16:19:07.783 回答
1
struct Student_info {
  Student_info& operator=(Student_info other) {
    swap(*this, other);
    return *this;
  }

  friend void swap(Student_info &a, Student_info &b) {
    using std::swap;
    #define S(N) swap(a.N, b.N);
    S(name)
    S(midterm)
    S(final)
    S(finalGrade)
    S(homework)
    #undef S
  }

private:
  std::string name;
  double midterm;
  double final;
  double finalGrade;
  std::vector<double> homework;
};

为简洁起见,省略了 getter、setter 和其他成员函数

如果您有只是样板的公共 getter 和 setter,请考虑将相应的数据成员标记为公共。

于 2011-01-26T16:41:18.497 回答