动态数组的优化

81 阅读2分钟

一 起点

老规矩,先上一段代码

class String{
   friend std::ostream& operator<<(std::ostream& stream,const String& string);

    private:
    char* m_buffer;
    unsigned int m_size;

    public:
    String(const char* string){
        m_size = strlen(string);
        m_buffer = new char[m_size+1];
        memcpy(m_buffer,string,m_size+1);
    }

    String(const String& other):m_size(other.m_size)
    {        
        std::cout<< "copy String"<<std::endl;
        m_buffer = new char[m_size+1];
        memcpy(m_buffer,other.m_buffer,m_size+1);
    }
    ~String(){
        delete[] m_buffer;
    }

    char& operator[](unsigned int index)
    {
        return m_buffer[index];
    }
};

void PrintString(String& string){
    std::cout<<string <<endl;
}

std::ostream& operator<<(std::ostream& stream,const String& string)
{
    stream<<string.m_buffer;
    return stream;
}

int main(){
    std::vector<String> v;
    v.push_back("a1");
    v.push_back("a2");

    // for(String& item : v){
    //     PrintString(item);
    // }
    return 0;
}

看到输出

copy String
copy String
copy String

奇怪,这里为什么会有三个日志输出。我一开始想也该只有两个,因为我只 push 了两次数据,第三次是哪儿来的呢?摸索了一番,发现这是 vector 扩容导致,因为 vector 默认容器大小为 1,当我们再 push 第二个数据的时候,它需要创建一个新的容器,然后把之前的数据给拷贝过去。可想而知,当我们的数据量越来越大的时候这得做多少的复制,太浪费性能了。所以这里我们的优化点一就是提前设置容器大小

    std::vector<String> v;
    v.reserve(2);
    v.push_back("a1");
    v.push_back("a2");

这样就只会输出两个 copy String 日志。那么这里我们可以进一步做一个优化。

    std::vector<String> v;
    v.reserve(2);
    v.emplace_back("a1");
    v.emplace_back("a2");

这样改后,不会再执行任何的拷贝操作了,完美!

  • emplace_back 可以直接在容器中构造新元素,避免了额外的拷贝或移动操作,因此在性能上可能更优。当你需要添加的元素类型有昂贵的拷贝或移动操作时,使用 emplace_back 是一个更好的选择。
  • push_back 需要传递一个与容器元素类型相同的对象,这可能导致额外的拷贝或移动操作。在一些简单类型或者已有对象的情况下,使用 push_back 也是合适的。

在实际编程中,根据具体情况选择使用 emplace_back 或 push_back。如果性能是关键因素,或者添加的元素类型有昂贵的拷贝或移动操作,那么优先考虑使用 emplace_back