在需要深度复制的结构上编码 std::sort

Coding std::sort on struct that needs deep copy

提问人:Charles 提问时间:8/10/2023 更新时间:8/11/2023 访问量:149

问:

我有一个结构体

typedef unsigned int    gsk_uint32;
typedef struct _gsk_oid {
    int                 count;
    gsk_uint32 *        elements;
} gsk_oid;
struct oidX : public gsk_oid
{
    oidX();     // Default
    oidX(int countX, gsk_uint32 first, ...);        // From list of integers
    oidX(const char* oidString);        // From dotted notation
    oidX(const gsk_oid *inOID);     // From gsk_oid
    ~oidX();        // destructor
};

我有一个结构,上面有 OidX 作为成员:

struct oidPair {
    oidX *TheOID;
    const char *Descript;
    oidPair(oidX *inputOID, const char *inputDescript); 
    ~oidPair();
};

我定义了上述结构的数组

oidPair testOIDs[4] = {
    oidPair(new oidX("2.23.140.1.2.3"), "Certificates issued in accordance with the CA/Browser Forum's Baseline Requirements - Individual identity asserted"),
    oidPair(new oidX("2.23.140.1.1"), "Extended Validation (EV) guidelines certificate policy"),
    oidPair(new oidX("1.3.6.1.4.1.4146.1.20"), "Organization validation certificate policy"),
    oidPair(new oidX("2.23.140.1.2.2"), "Certificates issued in accordance with the CA/Browser Forum's Baseline Requirements - Organization identity asserted") };

是的,我可以按排序顺序定义它们,但元素比我上面列出的要多得多,我想避免粗心大意的陷阱,所以在启动时我想对上面的数组进行排序。我发行std::sort(testOIDs, testOIDs+4, &Oid_less);

下面是比较函数:

int DecodeOID::Oid_cmp(const gsk_oid *left, const gsk_oid *right)
{
    int cnt = std::min(left->count, right->count);      // Shorter of the two vectors
    int ret = memcmp(left->elements, right->elements, cnt*4); // compare elements
    if ( 0 != ret ) return ret;  // if unequal we are done
    return left->count - right->count;      // else longer is considered greater
}
bool DecodeOID::Oid_less(const oidPair &left, const oidPair &right)
{
    const gsk_oid *l = left.TheOID;
    const gsk_oid *r = right.TheOID;
    int ret = Oid_cmp(l, r);
    return ret < 0;
}

排序第三次调用 Oid_less &right,引用具有未初始化 OidX 的 oidPair,并且因内存异常而失败。我知道我可能需要一个移动运算符或类似的东西,但我不知道到底要编码什么。建议表示赞赏。

C++ 排序 move-constructor

评论

3赞 user4581301 8/10/2023
您将需要复制或移动构造函数以及复制或移动赋值运算符。我建议从复制和交换成语开始,因为它几乎是万无一失的,而且通常足够快。分析会让你很快知道它是否不够快,在这一点上,如果需要,你可以问一个非常有针对性的问题。
2赞 ShadowRanger 8/10/2023
您尚未在此处提供最小的可重现示例。我们不知道结构成员的所有权是如何运作的(它们是借来的吗?你为什么不遵守三/五的规则?为什么不为您使用或管理内存呢?如果您手动管理内存或其他资源,那么 3/5 法则非常重要,在这种情况下,普通的构造函数和析构函数是不够的。我同意@user4581301的观点,即就简单性/正确性而言,复制和交换成语是处理这个问题的最佳方式。std::unique_ptrstd::shared_ptr
0赞 user4581301 8/10/2023
旁注:查看是否可以迁移到初始值设定项列表。通常比变量参数列表更容易使用,也更安全。oidX(int countX, gsk_uint32 first, ...);
1赞 paddy 8/10/2023
首先,你似乎不尊重三(或五)法则。此外,从 C++ 11 开始,如果正确支持移动语义,则排序操作不需要任何复制。您似乎意识到了这一点,因此您需要做的就是查阅参考资料,了解移动构造函数/移动赋值运算符的实际要求。使用复制和交换成语的建议是一个完美的起点。在实践中,在实现移动语义时,交换被经常使用。oidPair
1赞 PaulMcKenzie 8/10/2023
@Charles 在需要深度复制的结构上编码 std::sort -- 另一种选择是不对本身进行排序,而是使用索引数组进行排序。请参阅此示例,并阅读答案,尤其是第 3 项。你的结构可以像他们想要的那样疯狂,如果它们没有被移动,那么仍然可以使用链接中的方法进行排序struct

答:

0赞 Charles 8/11/2023 #1

谢谢大家。是的,向 oidPair 添加副本和 operator=() (基于 swap)解决了这个问题。我专注于发生异常的 OidX,但当然是正在排序的 oidPair。

开销不是一个主要问题。此时,该表有 22 个项目,可能不会超过两倍。它几乎以正确的顺序开始,因此交换的次数很少。

    friend void swap(oidPair& first, oidPair& second) // nothrow
    {
        using std::swap;
        swap(first.TheOID, second.TheOID);
        swap(first.Descript, second.Descript);
    }
    oidPair& operator=(oidPair other) 
    {
        swap(*this, other); 
        return *this;
    }
    oidPair(const oidPair &key)      // System copy
    {
        this->TheOID = new oidX(key.TheOID);
        this->Descript = key.Descript;
    }