类中包含的 STL 字符串成员包含在 std::vector 中

STL string member in class contained in std::vector

本文关键字:包含 std vector 字符串 STL 成员      更新时间:2023-10-16

我有一个c++类Foo,其中有一个stl std::string作为成员。此外,我有一个 STL std::vector<Foo> vecFoo,它按值包含类 Foo 的对象(而不是指向 Foo 对象的指针)。现在,一位朋友建议我不要以这种方式实现它,而是使用指针构建向量:std::vector<Foo*>或使用boost智能指针。他讨论了一个向量,它涉及对其成员的大量复制操作(在添加成员等时分配更多空间)当包含的类具有像std::string这样的动态成员时会产生问题。可能会出现任何问题吗?

据我了解,当类 Foo 可能被std::vector复制时,std::string实际上会执行深度复制(或写入时复制),因为 Foo 为其所有成员调用复制构造函数。我的朋友认为,当向量分配新空间时,如果向量中所有 Foo 对象之间的字符串成员长度不同,这是一个问题。你觉得怎么样?

使用指针在矢量内部Foo的唯一原因是速度。指向Foo(Foo*)的指针复制速度比完整的类Foo快得多,不是吗?谢谢讨论!

我想

,如果您要使用 new 分配每个元素对象,那么使用指针std::vector根本不会更有效。但是,如果你的元素对象足够大(不是因为字符串长度,而是因为它的"非指针"成员),带有指针的向量可以表现得更好或不 - 这取决于向量重新分配频率。

考虑一下std::vector很少分配空间对数的事实,因为它分配的空间总是比以前分配的时间多两倍。

C++11 的情况甚至更好,std::vector将使用移动语义来避免复制字符串字符。但是,如果您的编译器未生成默认的移动构造函数,则可能需要在类中插入移动构造函数才能使此操作正常工作。

甚至在 C++11 之前,一些std::string实现就使用写入时复制策略,因此仅制作字符串的"深度复制"不需要复制底层字符数组,除非其中一个副本被修改。

如果字符串成员的长度不同,则存在问题 向量中的所有 Foo 对象

这肯定不是问题 - 当然std::string对象在内部包含指向字符数组的指针,而不是数组本身。

我敢肯定,在做出此类级别的优化决策之前,您应该使用分析器分析您的程序

这里有一个权衡。你的Foo是否足够复杂,以至于复制它比复制指针或智能指针要贵得多?您是否希望矢量在执行期间频繁调整大小,从而导致复制?或者,复制Foo向量的成本是否可以接受,而不是使用指针(之后必须清理)或智能指针的额外复杂性?在矢量中存储指针有其自身的成本,因为指向的对象不一定在内存中是连续的。(但是,当按值存储时,Foos 中包含的字符串将数据分散在内存中。

尝试简单的实现(Foo的向量),测量其性能,然后才考虑切换到(智能)指针的优化。

您有两种选择:

    使用 std::
  1. unique_ptr 或 std::shared_ptr
  2. 按值使用它并提供移动语义

如果你使用第二个选择 你必须以这种方式定义Foo。

struct Foo
{
    // default ctor
    Foo( const std::string& s = "" ) : name( s ) {}
    // copy ctor
    Foo( const Foo& rhs ) : name( rhs.name ) {}
    // move ctor
    Foo( Foo&& rhs ) : name( std::move( rhs.name ) ) {}
    // assign operator
    Foo& operator = ( const Foo& f )
    {
        name = f.name;
        return *this;
    }
    // move operator
    Foo& operator = ( Foo&& f )
    {
        std::swap( name, f.name );
        return *this;
    }
    string name;
};
FooVec v;
// construct in place
v.emplace_back( "foo1" );
v.emplace_back( "foo2" );
// or move
v.push_back( std::move( Foo( "foo3" ) );