【发布时间】:2020-10-31 02:11:09
【问题描述】:
我需要帮助来理解为什么 std::set<T>::insert(T) 调用 operator<(const T&, const T&) 时使用无效引用作为其第二个参数,从而在我第一次插入时创建了段错误。
这是我想在集合中插入其对象的类:
/**
* A CPC category.
*/
class Category {
private:
char m_section;
int m_class;
char m_subclass;
int m_group;
int m_subgroup;
public:
Category();
Category(char t_section, int t_class, char t_subclass, int t_group,
int t_subgroup);
char get_section() const;
int get_class() const;
char get_subclass() const;
int get_group() const;
int get_subgroup() const;
};
这是我的operator<(抱歉,这不是最干净的方法):
/**
* Obeys simple lexical order. A smaller-than operator for Category is
* required to allow objects to be placed in an std::set.
*/
bool operator<(const Category& cat1, const Category& cat2)
{
return (cat1.get_section() == cat2.get_section() ?
cat1.get_class() == cat2.get_class() ?
cat1.get_subclass() == cat2.get_subclass() ?
cat1.get_group() == cat2.get_group() ?
cat1.get_subgroup() == cat2.get_subgroup() ?
true :
cat1.get_subgroup() < cat2.get_subgroup() :
cat1.get_group() < cat2.get_group() :
cat1.get_subclass() < cat2.get_subclass() :
cat1.get_class() < cat2.get_class() :
cat1.get_section() < cat2.get_section());
}
这里是创建集合的代码:
std::istream& operator>> (std::istream& is, CategorySet& cs)
{
std::set<Category>* cats;
Category cat;
while (is >> cat) {
cats->insert(cat);
}
cs = CategorySet{ cats };
return is;
}
我在cats->insert(cat);之前放了一个断点:
Breakpoint 1, InnovationModelling::operator>> (is=..., cs=...) at lib/PatentData.cpp:126
126 cats->insert(cat);
(gdb) p *cats
$9 = std::set with 0 elements
(gdb) p cat
$1 = {m_section = 65 'A', m_class = 1, m_subclass = 65 'A', m_group = 1, m_subgroup = 0}
(gdb) ptype cat
type = class InnovationModelling::Category {
private:
char m_section;
int m_class;
char m_subclass;
int m_group;
int m_subgroup;
public:
Category(void);
Category(char, int, char, int, int);
char get_section(void) const;
int get_class(void) const;
char get_subclass(void) const;
int get_group(void) const;
int get_subgroup(void) const;
}
(上面的$9是因为我忘记表明此时集合是空的,所以我第二次运行程序。)
然后我遍历std::set的东西,直到它调用operator(),它并没有重载:
(gdb) s
std::less<InnovationModelling::Category>::operator() (this=0x5555555993c0 <std::cout@@GLIBCXX_3.4>, __x=..., __y=...)
at /usr/include/c++/10.2.0/bits/stl_function.h:386
386 { return __x < __y; }
(gdb) p __x
$2 = (const InnovationModelling::Category &) @0x7fffffffd3b0: {m_section = 65 'A', m_class = 1, m_subclass = 65 'A', m_group = 1,
m_subgroup = 0}
(gdb) p __y
$3 = (const InnovationModelling::Category &) <error reading variable>
果然,operator< 被这个无效引用调用,因此 get_section() 最终被调用并出现段错误:
(gdb) s
InnovationModelling::Category::get_section (this=0x26) at lib/PatentData.cpp:187
187 return m_section;
(gdb) p this
$7 = (const InnovationModelling::Category * const) 0x26
(gdb) p *this
Cannot access memory at address 0x26
(gdb) s
Program received signal SIGSEGV, Segmentation fault.
0x0000555555568412 in InnovationModelling::Category::get_section (this=0x26) at lib/PatentData.cpp:187
如果std::set::insert() 在做什么很明显,我很抱歉,但我是 C++ 的初学者,我什至无法理解它觉得有必要致电 operator<()根本就是在一个空集合中插入一些东西。
【问题讨论】:
-
我猜你的
operator<有问题,很可能是它没有正确实现严格的弱排序。它也几乎不可读。请考虑使用return std::tie(cat1.a, cat1.b, etc) < std::tie(cat2.a, cat2.b, etc)。 -
operator>>()创建一个未初始化的指针 (cats) 并反复取消引用它。这给出了未定义的行为。在取消引用它之前,它需要被初始化为指向一个有效的对象。一旦你的程序的行为未定义,所有的赌注都被取消了。包括您在std::setmisbeaving 中看到的其他功能的错觉。 -
您的
operator <在类别相同时返回true。这是一个错误。不过,可能不是导致问题的原因。 -
@Peter 这可能不是错觉,因为比较器很疯狂,但你也是对的
-
谢谢大家。我现在看到我的问题对其他人没有帮助。我应该保留它还是删除它?我已经在没有指针的情况下重新实现了这些类,它已经解决了这个问题以及我遇到的其他问题。我不确定为什么我选择在
operator<中实现预购,但我可以看到为什么它不能与<的语义一起工作,以及它如何与std::set混淆,所以我也修复了它。
标签: c++ segmentation-fault std stdset