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

时间:2013-10-31 10:42:58

标签: c++ string vector stl

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

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

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

3 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:2)

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

考虑std::vector很少分配空间 logarithmical 的事实,因为它总是分配比前一次分配的空间多两倍的空间。

使用C ++ 11,情况会更好,std::vector将使用move semantics来避免确保复制字符串的字符。但是如果你的编译器没有生成default move constructor,你可能需要在你的类中实现移动构造函数才能使这个东西工作。

即使在C ++ 11之前,一些std::string实现使用了写时复制策略,因此只需对字符串进行“深层复制”就不需要复制基础字符数组,除非其中一个副本被修改。

  

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

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

我确定,您应该使用分析器分析您的程序,然后才能在此级别做出优化决策。

答案 2 :(得分:0)

您有两种选择:

  1. 使用std :: unique_ptr或std :: shared_ptr
  2. 按值使用它并提供移动语义
  3. 如果您使用第二个选择您必须以这种方式定义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" ) );