将派生类实例插入std :: set

时间:2012-01-05 11:30:23

标签: c++ set std

考虑以下环境:

A.H

class A {
public:
A(double);
virtual bool operator==(const A&) const {return FALSE;};
virtual bool operator<(const A&) const {return FALSE;};
double[] values;
int id;
}

B.h

class B : public A {
B(double);
bool operator==(const A&) const ;
bool operator<(const A&) const;
}

C.cpp

std::set<A> myset;
for (unsigned int i = 0; i < 10; i++) {
  B tempElement = B((double)i);
  myset.insert(tempElement);
  std::cout << myset.size() << std::flush;
}

我想在我的新集合中添加10个元素,但C.cpp(1111111111)中最后一行的输出告诉我,集合中始终只有一个元素。在循环结束后,调用B析构函数。如何防止删除对象并将其插入到设备中?我是否必须实现特殊的复制构造函数或我做错了什么?

编辑:关于对象切片:假设扩展类中没有新成员。只有运营商的定义方式不同......

6 个答案:

答案 0 :(得分:4)

这里有对象切片的问题。 std::set保存您插入的对象的副本,A的复制构造函数构造A类型的对象。因此,该集合仅有效地存储您插入的第一个A对象的B部分。您需要boost::ptr_set之类的东西来存储派生类的实例。

我不确定为什么插件没有发生(代码太少),但我认为任何B对象都等于切片B对象。

答案 1 :(得分:4)

您面临的问题是双重问题:

  • 您没有在集合中插入B,而是插入A s(这是切片问题)
  • operator<未正确定义A

我将跳过对象切片问题,它有详细记录。如果您使用纯虚函数(而不是无意义的定义),您的代码将无法编译,从而防止出现问题。

现在,从数学上讲,每当定义operator<时,它应该定义:

在这里,operator<的“虚拟”定义不能成为反对称关系,因此天真地期望这种算法的算法(例如集合插入)突然发现自己给出incoherent results

  

有两次我被问到 - “请问巴贝奇先生,如果你把错误的数字输入机器,那么正确的答案会出来吗?” ......我无法理解可能引发这样一个问题的想法混淆。

     

- Charles Babbage来自哲学家生活的段落

我还要注意operator==的虚拟定义不是等价关系(即a == a不成立),但是==不用于集合,所以它是不是问题的原因。

我强烈建议你避免定义像这样的虚假行为。它只会导致混乱的错误。相反,您应该使这些虚拟方法变得纯粹(以便在编译期间检测到这些问题),或者至少抛出异常。

答案 2 :(得分:0)

如果正如您所说,派生类没有任何额外成员,那么这不是对象切片的常见情况。

但是,请考虑如何初始化vtable:您传递了对B实例的引用;这将隐式转换为A const &,以便该集合可以复制构造A。这个A 将是A :它无法复制B的vtable,因此它不会调用B个覆盖的运算符。< / p>

例如,请参阅原型模式,该模式使用虚拟clone方法来允许虚拟复制。复制构造函数当然不会提供此行为。

至于解决方案:存储指针,就像其他人都说的那样。

答案 3 :(得分:0)

好的,所以我在c ++中很新,但我理解这个问题: 1)离开循环后调用析构函数是破坏仅存在于循环内的变量的结果。所有自动化变量都有执行它们所定义的块的生命周期。换句话说,当你启动循环时,创建对象并在堆栈上分配内存,当你离开循环时,内存被释放,所以析构函数被调用。要避免这种影响,您应该使用poiters和动态内存分配。 2)我认为1)导致向集合中添加元素的问题

一般来说,我认为你在尝试使用非二进制内存分配动态创建对象时会犯一个错误。

如果我错了,请纠正我。

答案 4 :(得分:0)

主要问题在于operator<。您使用的定义(始终返回FALSE)有效地告诉集合A的所有实例彼此相等。结果,只有第一个A被接受到集合中。您应该将A::operator<更改为

virtual bool operator<(const A&a) const {return this->id < a.id;};

virtual bool operator<(const A&a) const {return this->values[0] < a.values[0];};

或类似的东西。 A和B的构造函数是什么,id和值是如何初始化的?

您还有其他潜在问题,例如其他人提到的切片。但operator<是目前的主要问题。

答案 5 :(得分:0)

你认为自己没有切片是错误的,因为你仍在创建一个A的实例,这意味着运算符的基类成员&lt;和operator ==被调用。

std :: set将两个元素视为相等,如果是x