最近在刷PAT甲级辅导课的时候,遇到了如标题所示的错误。题目链接:1561. PAT 评测 - AcWing题库
引起错误的代码如下:
#include<bits/stdc++.h>
using namespace std;
int N,M;
// const int K = 6;
int n, k, m;
int P[K];
int K = 6;
struct stu
{
string id;
int grade[K];
int total = 0;
int count;
// stu()
// {
// cout << "默认构造函数执行" << endl;
// }
stu(const stu& oth)
{
this->id = oth.id;
memcpy(this->grade, oth.grade, 4*k);
this->total = oth.total;
this->count = oth.count;
cout << "自定义拷贝构造函数执行" << endl;
}
stu(string _id):id(_id)
{
for(int i = 1; i <= k; ++i)
grade[i] = -2;
total = count = 0;
cout << "自定义构造函数执行" << endl;
}
stu& operator = (const stu& oth)
{
this->id = oth.id;
// this->grade = oth.grade;
memcpy(this->grade, oth.grade, 4*k);
this->total = oth.total;
this->count = oth.count;
return *this;
cout << "重载的赋值操作符执行" << endl;
}
void calc()
{
for(int i = 1; i <= k; ++i)
{
total += max(0, grade[i]);
count += grade[i] == P[i];
}
}
bool has_submit()
{
for(int i = 1; i <= k; ++i)
{
if(grade[i] >= 0)
return true;
}
return false;
}
bool operator < (const stu& oth)
{
if(this->total != oth.total)
return this->total > oth.total;
else if(this->count != oth.count)
return this->count > oth.count;
else
return this->id < oth.id;
}
};
int main()
{
unordered_map<string, stu> students;
cin >> n >> k >> m;
for(int i = 1; i <= k; ++i) cin >> P[i];
while(m--)
{
string u_id;
int p_id, grade;
cin >> u_id >> p_id >> grade;
if(students.count(u_id) == 0)
students[u_id] = stu(u_id); //引起出错的地方
students[u_id].grade[p_id] = max(students[u_id].grade[p_id],
grade);
}
vector<stu> res;
for(auto& [fi, se]: students)
{
if(se.has_submit())
{
se.calc();
res.push_back(se);
}
}
sort(res.begin(), res.end());
for(int i = 0, rank=1; i < res.size(); ++i)
{
if(i && res[i].total != res[i-1].total)
rank = i+1;
cout << rank << " " << res[i].id << " " << res[i].total << " ";
for(int j=1; j <= k; ++j)
{
if(res[i].grade[j] == -2)
cout << "-" ;
else
cout << max(res[i].grade[j], 0) ;
if(j < k)
cout << " ";
}
cout << endl;
}
return 0;
}
引起错误的地方:
按理说,在上述代码中我已经满足了C++的三五法则:完成了拷贝构造函数的自定义实现,但还是报错了。最后经过调试发现,出错的地方是因为没有默认构造函数导致的。
如下测试代码就没有问题:
#include <bits/stdc++.h>
using namespace std;
const int K = 6;
const int k = 5;
const int P[1000] = {0};
struct stu
{
string id;
int grade[K];
int total = 0;
int count;
// stu()
// {
// cout << "默认构造函数执行" << endl;
// }
stu(const stu &oth)
{
this->id = oth.id;
memcpy(this->grade, oth.grade, 4 * k);
this->total = oth.total;
this->count = oth.count;
cout << "自定义拷贝构造函数执行" << endl;
}
stu(string _id) : id(_id)
{
for (int i = 1; i <= k; ++i)
grade[i] = -2;
total = count = 0;
cout << "自定义构造函数执行" << endl;
}
stu &operator=(const stu &oth)
{
this->id = oth.id;
// this->grade = oth.grade;
memcpy(this->grade, oth.grade, 4 * k);
this->total = oth.total;
this->count = oth.count;
return *this;
cout << "重载的赋值操作符执行" << endl;
}
void calc()
{
for (int i = 1; i <= k; ++i)
{
total += max(0, grade[i]);
count += grade[i] == P[i];
}
}
bool has_submit()
{
for (int i = 1; i <= k; ++i)
{
if (grade[i] >= 0)
return true;
}
return false;
}
bool operator<(const stu &oth)
{
if (this->total != oth.total)
return this->total > oth.total;
else if (this->count != oth.count)
return this->count > oth.count;
else
return this->id < oth.id;
}
};
int main()
{
stu A("AAA");
stu B(A);
stu C = A;
return 0;
}
同样是没有默认构造函数,但是这个测试代码就能正常工作。因此初步推断当一个类对象作为哈希表的值时,该类必要要有默认的无参构造函数。并且经过调试发现:
1、无论是Windows的编译器还是linux的编译器,当一个类对象作为哈希表的值时,用该类的另一个对象对它进行初始化时,该类是先调用默认无参构造函数后再调用重载的赋值操作符,这可能跟哈希表的底层实现有关系。
2、当该类的实例对象不作为哈希表的值的时候,用一个已有的实例去初始化一个新的同类实例,无论是通过stu B(A) 还是通过stu B = A的方式都是直接走的拷贝构造函数方式。
所以在开发过程中即使类重载了多种构造函数,最好还是保留默认构造函数,多人协同开发中指不定别人会拿你的类作为哈希表的值呢。亦或者是给参数都赋默认值,这也是个可行办法。
另外,上述代码是过不了原题的,需要将上述中的拷贝构造函数和重载的复制操作符注释掉才行,原因应该是跟memcpy拷贝grade这一块代码有关系。