【问题标题】:Extending a vector iterator to access data members of the contained class/structure扩展向量迭代器以访问包含的类/结构的数据成员
【发布时间】:2011-08-28 19:27:42
【问题描述】:

所以,我有一个类基于附加到字符串中每个字符的元数据进行字符串操作。在内部,我用std::vector<CharacterType> 表示它,其中CharacterType 包含有问题的字符等。这种内部表示对我很有用,但在操作结束时,类用户对字符串本身感兴趣。

为了做到这一点,我决定扩展std::vector<CharacterType>::iterator 以包含一个返回字符而不是CharacterTypeoperator*() 方法。这是代码,它适用于 Windows 7 上的 Visual Studio 2008。但它是惯用的 C++ 吗?自 90 年代以来,我没有写太多 C++,所以如果我在做一些危险和/或邪恶的事情,我会很感激一些关于这种风格的反馈。 (在下面的代码中,我做了一个简化的CharacterType 结构,称为mytype——实际上这个结构要大得多。)

#include <iostream>
#include <vector>
#include <string>

struct mytype {
    char c;
};

class myit : public std::vector<mytype>::iterator {
public:
    inline myit(std::vector<mytype>::iterator const &c)
        : std::vector<mytype>::iterator(c) {}
    char operator*() {
        const mytype &p =
            std::vector<mytype>::iterator::operator*();
        return p.c;
    }

    // Added these in a later edit, after thinking about a comment below
    typedef char value_type;
    typedef char *pointer;
    typedef char &reference;

private:
};

int
main()
{
    mytype test[] = { {'a'}, {'b'}, {'c'}, {'d'} };
    std::vector<mytype> vec(&test[0], &test[4]);
    myit i(vec.begin()), e(vec.end());
    std::string str(i, e);

    std::cout << str << std::endl;
    return 0;
}

假设这是一种很好的风格,是否适合使用 C++ 类型感知方法重载在 myit 中拥有多个 operator*() 方法,以便我可以使用相同的迭代器类来获取不同类型的另一个成员?还是使用模板会更好?例如,一个元数据是角色的语言,我希望能够以完全相同的方式提取std::vector&lt;LanguageType&gt;。想法?

此外,由于返回这些数据的方法不会影响对象的内部表示,为了 const 的正确性,我希望能够定义访问器 const。这可能意味着我需要修改上面的内容以使用const_iterator,但我还没有做到这一点。

提前感谢您的风格评论!

【问题讨论】:

    标签: c++ string vector iterator


    【解决方案1】:

    您不应该派生 std::vector (也不是其他 STL 容器),因为它的析构函数不是虚拟的,并且您可能会遇到未调用向量的析构函数并且出现内存泄漏的情况。

    【讨论】:

    • 嗯,正如 Sergio 指出的那样,我实际上是在派生一个迭代器。但是,更一般地说,如果我不声明析构函数,我可以派生吗,还是会阻止基类的析构函数触发?显式调用基类的析构函数怎么样?我认为目前不需要派生 STL 容器,但了解这些东西总是好的。
    • 对,不好意思,看错了,好像是我没注意。
    【解决方案2】:

    首先,我也希望能够在这么长时间的休息之后写出这么好的 C++,如果它发生在我身上的话!

    说,排除你的CharacterTypechar的简单转换运算符的原因是什么?类似:CharacterType::operator char();

    当然,我们都知道运算符重载的问题(但在这两种情况下不会改变)...

    关于你问的其他事情,你不能根据返回类型重载,所以你不可能有不同的 operator() 用于不同的返回类型,如果这就是你的意思的话。

    但正如你所提到的,你可以为此使用模板。

    【讨论】:

    • 感谢您的客气话。老实说,我没有想到我可以使用转换运算符,我忘记了那些存在。我会调查他们。此外,您对返回类型多态性也是正确的。我可能需要在这里复制一些样板。
    【解决方案3】:

    我不相信你可以在同一个班级中有“几个operator*()重载”;大概你会想根据返回类型来消除歧义,但 C++ 不会这样做。

    另外,直接从不相关的迭代器类型派生可能不是一个好主意。为了作为 STL 迭代器正确运行,迭代器类需要定义许多 typedef 等。如果像这样直接继承它们,你的任何一个都将没有意义。有关更多信息,请参见例如这篇文章在Custom Containers & Iterators for STL-Friendly Code.

    【讨论】:

    • 是的,你当然是对的(关于返回类型多态性),我的错。仍然回到 C++ 领域。我会看看你的链接,谢谢。
    • Oli,在考虑了您的评论后,我决定继续适当地定义value_typepointerreference,我认为这应该使课程更加一致。你怎么看?再次感谢。
    【解决方案4】:

    您通过从 operator*() 返回 CharacterType 以外的类型来破坏多个迭代器协定。老实说 - 如果您迫切希望 CharacterType 可转换,只需给 CharacterType 一个 operator char()

    【讨论】:

    • 我添加到迭代器实例中的 typedef 是否完全改变了你的想法,还是没有帮助?
    【解决方案5】:

    一些不同的人建议我研究转换运算符,我必须承认我完全忘记了。基于转换运算符的解决方案相当干净:

    #include <iostream>
    #include <vector>
    #include <string>
    
    struct mytype {
        char c;
        inline operator char () { return c; }
    };
    
    int
    main()
    {
        mytype test[] = { {'a'}, {'b'}, {'c'}, {'d'} };
        std::vector<mytype> vec(&test[0], &test[4]);
        std::string str(vec.begin(), vec.end());
    
        std::cout << str << std::endl;
        return 0;
    }
    

    ...而且它并不涉及我担心听起来不祥的事情,例如违反 STL 迭代器合同。另外,似乎(尽管我还没有尝试过)如果我希望能够轻松复制字符串以外的其他内容(我原始问题中的 std::vector&lt;LanguageType&gt; 想法),我可以通过添加一个不同的cast 运算符,而迭代器派生技术需要更多样板,因为 C++ 中没有返回类型多态性,正如几个人向我指出的那样。

    非常感谢您的回复,一旦我让代表这样做,我会为大家投票,以防您想知道为什么我还没有。

    【讨论】:

    • 您也可以考虑使用std::basic_string&lt;mytype&gt; 而不是std::vector&lt;mytype&gt;std::basic_string 是字符串类型的模板类(std::string 只是 typedefstd::basic_string&lt;char&gt;)。这将允许您的mystring 类型更像字符串(而不是必须使用std::vector,使其类似于矢量)。您需要全面了解std::char_traits 并将其专门用于mytype,但总而言之,我认为这会使您的代码比尝试使用std::vector 来存储看似字符串的内容更容易。
    • @ChrisLutz 感谢您的提示。实际上,尽管对于我正在尝试做的事情来说,vector 比 basic_string 更好;我确实简要地看了它。归根结底,不得不“丢弃”元数据的问题是一样的,而且对于我正在做的一些事情来说,矢量更方便一些。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-16
    • 2022-01-19
    • 1970-01-01
    • 1970-01-01
    • 2018-03-16
    • 1970-01-01
    相关资源
    最近更新 更多