C++ vector插入元素(数据)详解
通过使用成员函数 emplace(),可以在 vector 序列中插入新的元素。对象会在容器中直接生成,而不是先单独生成对象,然后再把它作为参数传入。
emplace() 的第一个参数是一个迭代器,它确定了对象生成的位置。对象会被插入到迭代器所指定元素的后面。第一个参数后的参数,都作为插入元素的构造函数的参数传入。例如:
"first" "AAAAA" "$$$$" "second"
在 emplace() 的第一个参数的后面,可以使用尽可能多的参数,只要它们是被插入对象的构造函数所需要的。在上面的代码片段中,第一次调用 emplace() 会得到一个由构造函数 string(5,'A') 生成的字符串对象。emplace() 会返回一个指向横入元素的迭代器,被用来在插入元素的后面,插入一个新的元素。
成员函数 insert() 可以在 vector 中插入一个或多个元素。第一个参数总是一个指向插入点的 const 或 non-const 迭代器。元素会被迅速插入到第一个参数所指向元素的前面,如果第一个参数是一个反向迭代器,元素会被插入到迭代器所指向元素的后面。如果选择使用 insert() 来插入元素,稍后会分别阐述每一种可能的情况。会先定义一个 vector,然后列出一个相继调用 insert() 的列表:
执行完这条语句后,words rector 容器包含的字符串元素为:
"one" "two" "three" "eight"
返回的迭代器指向被插入的元素 string(”two”)。需要注意的是,在使用同样参数的情况下,调用 insert() 没有调用 emplace() 高效。在 insert() 调用中,构造函数调用 string("two") 生成了一个对象,作为传入的第二个参数。在 emplace() 调用中,构造函数用第二个参数直接在容器中生成了字符串对象。
"one" "two" "three" "five" "six" "seven" "eight"
返回的迭代器指向插入的第一个元素"five"。
"one" "two" "three" "five" "six" "seven" "eight" "ten"
返回的迭代器指向插入的元素"ten”。这和上面的情况 1) 相似;这表明,当第一个参数不指向元素而是指向最后一个元素之后的位置时,它才发挥作用。
执行完这条语句后,words vector 容器中的字符串对象如下:
"one" "two" "three" "five" "six" "seven" "eight" "nine" "nine" "ten"
返回的迭代器指向插入的第一个元素"nine"。注意,示例中的第一个参数是一个 const 迭代器,这也表明可以使用 const 迭代器。
"one" "two" "three" "five" "six" "seven" "eight" "nine" "nine" "ten" "twelve" "thirteen"
返回的迭代器指向插入的第一个元素"twelve"。初始化列表中的值必须和容器的元素类型相匹配。T 类型值的初始化列表是std::initializer_list<T>,所以这里的 list 类型为 std::initializer_list<std::string>。前面的 insert() 调用中以单词作为参数的地方,参数类型是 std::string,所以单词作为字符串对象的初始值被传入到函数中。
记住,所有不在 vector 尾部的插入点都会有开销,需要移动插入点后的所有元素,从而为新元素空出位置。当然,如果插入点后的元素个数超出了容量,容器会分配更多的内存,这会增加更多额外开销。
vector 的成员函数 insert(),需要一个标准的迭代器来指定插入点;它不接受一个反向迭代器——这无法通过编译。如果需要查找给定对象的最后一个元素,或者在它的后面插入一个新的元素,就需要用到反向迭代器。这里有一个示例:
调用 riter 的成员函数 base() 可以得到一个标准迭代器,从序列反方向来看,它指向 riter 前的一个位置,也是朝向序列结束的方向。因为 riter 指向第三个元素,也就是“one”,所以 riter.base() 指向第 4 个元素“three”。如果使用 riter.base() 作为 insert() 的第一个参数,“five”将被插入到这个位置之前,也就是 riter 所指向元素的后面。执行完这些语句后,str 容器会包含下面 5 个字符串元素:
"one", "two", "one", "five", "three"
如果想把插入点变成 fmd() 返回位置的前一个位置,需要将 insert() 的第一个参数变为 iter.base()-1。
emplace() 的第一个参数是一个迭代器,它确定了对象生成的位置。对象会被插入到迭代器所指定元素的后面。第一个参数后的参数,都作为插入元素的构造函数的参数传入。例如:
std::vector<std::string> words {"first", "second"}; // Inserts string(5,'A') as 2nd element auto iter = words.emplace(++std::begin(words),5,'A'); //Inserts string ("$$$$") as 3rd element words.emplace(++iter, "$$$$");这段代码执行后,vector 中的字符串对象如下:
"first" "AAAAA" "$$$$" "second"
在 emplace() 的第一个参数的后面,可以使用尽可能多的参数,只要它们是被插入对象的构造函数所需要的。在上面的代码片段中,第一次调用 emplace() 会得到一个由构造函数 string(5,'A') 生成的字符串对象。emplace() 会返回一个指向横入元素的迭代器,被用来在插入元素的后面,插入一个新的元素。
成员函数 insert() 可以在 vector 中插入一个或多个元素。第一个参数总是一个指向插入点的 const 或 non-const 迭代器。元素会被迅速插入到第一个参数所指向元素的前面,如果第一个参数是一个反向迭代器,元素会被插入到迭代器所指向元素的后面。如果选择使用 insert() 来插入元素,稍后会分别阐述每一种可能的情况。会先定义一个 vector,然后列出一个相继调用 insert() 的列表:
std::vector<std::string> words { "one","three","eight"} //Vector with 3 elements下面介绍一些使用 insert() 插入单词的方式:
1) 插入第二个参数指定的单个元素
auto iter = words.insert(++std::begin(words), "two");在这个示例中,插入点是由 begin() 返回的迭代器递增后得到的。它对应第二个元素,所以新元素会作为新的第二个元素插入,之前的第二个元素以及后面的元素,为了给新的第二个元素留出空间,都会向后移动一个位置。这里有两个 insert 重载版本,它们都可以插入单个对象,其中一个的参数是 constT& 类型,另一个是 T&&类 型——右值引用。因为上面的第二个参数是一个临时对象,所以会调用第二个函数重载版本,临时对象会被移动插入而不是被复制插入容器。
执行完这条语句后,words rector 容器包含的字符串元素为:
"one" "two" "three" "eight"
返回的迭代器指向被插入的元素 string(”two”)。需要注意的是,在使用同样参数的情况下,调用 insert() 没有调用 emplace() 高效。在 insert() 调用中,构造函数调用 string("two") 生成了一个对象,作为传入的第二个参数。在 emplace() 调用中,构造函数用第二个参数直接在容器中生成了字符串对象。
2) 插入一个由第二个和第三个参数指定的元素序列
std:: string more[] {"five", "six", "seven" }; // Array elements to be inserted iter = words.insert(--std::end(words) , std::begin(more), std::end(more));第二条语句中的插入点是一个迭代器,它是由 end() 返回的迭代器递减后得到的。对应最后一个元素,因此新元素会被插入到它的前面。执行这条语句后,words vector 容器中的字符串对象为:
"one" "two" "three" "five" "six" "seven" "eight"
返回的迭代器指向插入的第一个元素"five"。
3) 在 vector 的末尾插入一个元素
iter = words.insert(std::end(words), "ten");插入点是最后一个元素之后的位置,因此新元素会被添加到最后一个元素之后。执行完这条语句后,words vector 容器中的字符串对象如下:
"one" "two" "three" "five" "six" "seven" "eight" "ten"
返回的迭代器指向插入的元素"ten”。这和上面的情况 1) 相似;这表明,当第一个参数不指向元素而是指向最后一个元素之后的位置时,它才发挥作用。
4) 在插入点插入多个单个元素。第二个参数是第三个参数所指定对象的插入次数
iter = words.insert(std::cend(words)-1, 2, "nine");插入点是最后一个元素,因此新元素 string("nine") 的两个副本会被插入到最后一个元素的前面。
执行完这条语句后,words vector 容器中的字符串对象如下:
"one" "two" "three" "five" "six" "seven" "eight" "nine" "nine" "ten"
返回的迭代器指向插入的第一个元素"nine"。注意,示例中的第一个参数是一个 const 迭代器,这也表明可以使用 const 迭代器。
5) 在插入点,插入初始化列表指定的元素。第二个参数就是被插入元素的初始化列表
iter = words.insert(std::end(words), {std::string {"twelve"},std::string {"thirteen"}});插入点越过了最后一个元素,因此初始化列表中的元素被添加到容器的尾部。执行完这条语句后,words vector 容器中的字符串对象如下:
"one" "two" "three" "five" "six" "seven" "eight" "nine" "nine" "ten" "twelve" "thirteen"
返回的迭代器指向插入的第一个元素"twelve"。初始化列表中的值必须和容器的元素类型相匹配。T 类型值的初始化列表是std::initializer_list<T>,所以这里的 list 类型为 std::initializer_list<std::string>。前面的 insert() 调用中以单词作为参数的地方,参数类型是 std::string,所以单词作为字符串对象的初始值被传入到函数中。
记住,所有不在 vector 尾部的插入点都会有开销,需要移动插入点后的所有元素,从而为新元素空出位置。当然,如果插入点后的元素个数超出了容量,容器会分配更多的内存,这会增加更多额外开销。
vector 的成员函数 insert(),需要一个标准的迭代器来指定插入点;它不接受一个反向迭代器——这无法通过编译。如果需要查找给定对象的最后一个元素,或者在它的后面插入一个新的元素,就需要用到反向迭代器。这里有一个示例:
std::vector<std::string> str { "one", "two", "one", "three"}; auto riter = std::find(std::rbegin(str), std::rend(str) , "one"); str.insert(riter.base(), "five");fmd() 算法会在头两个参数所指定的一段元素中,搜索第三个参数指定的元素,返回第一个找到的元素,因此会找到 String("one")。它会返回一个迭代器,这个迭代器和用来指定搜索范围的迭代器有相同的类型,是一个指向匹配元素的反向迭代器。如果没有找到匹配的元素,那么它就是指向第一个元素之前位置的迭代器 rend(str)。使用反向迭代器意味着 fmd()会找到最后匹配的元素;使用标准迭代器会找到第一个匹配的元素,如果没有匹配的元素,会返回 end(str)。
调用 riter 的成员函数 base() 可以得到一个标准迭代器,从序列反方向来看,它指向 riter 前的一个位置,也是朝向序列结束的方向。因为 riter 指向第三个元素,也就是“one”,所以 riter.base() 指向第 4 个元素“three”。如果使用 riter.base() 作为 insert() 的第一个参数,“five”将被插入到这个位置之前,也就是 riter 所指向元素的后面。执行完这些语句后,str 容器会包含下面 5 个字符串元素:
"one", "two", "one", "five", "three"
如果想把插入点变成 fmd() 返回位置的前一个位置,需要将 insert() 的第一个参数变为 iter.base()-1。