从另一个列表的元素构造一个列表,而不增加堆内存

时间:2019-04-28 11:31:20

标签: c++ stdlist

我正在开发内存关键型应用程序。我首先生成一个列表,例如(C ++ 11):

std::list<string> nodes({"Hello","Welcome", "Hi", "World"});

我现在需要创建一个较小的列表,其中包含“节点”的第二个和第三个元素。天真地,我会这样做:

std::list<string> sub_nodes;
sub_nodes.push_back(*std::next(nodes.begin(),1));
sub_nodes.push_back(*std::next(nodes.begin(),2));

但这显然通过分配新的内存来为sub_nodes分配堆中的内存。

*(sub_nodes.begin()) = "NotWelcome"; //changing first element
std::list<string>::iterator it;
for(it=nodes.begin();it!=nodes.end();++it) cout<<*it<<'\t';
cout<<'\n';
//Hello Welcome Hi World
for(it=sub_nodes.begin();it!=sub_nodes.end();++it)
cout<<*it<<'\t';
cout<<'\n';
//NotWelcome Hi

我希望完成的工作是让sub_nodes的元素占据与创建它们的nodes元素相同的地址。换句话说,我希望对sub_nodes的元素所做的更改反映在nodes中的那些元素中,反之亦然。在C链表中,这很简单,因为列表节点基本上是指针。我将如何在C ++中完成相同的任务?

3 个答案:

答案 0 :(得分:1)

std::list<T>拥有其元素。那就是使用标准容器的主要动机。他们管理着元素的生命周期。如果您希望容器不拥有其元素,则不要使其存储T s。有几种选择。使用std::list的好处是std::list的迭代器不会轻易失效(例如,std::vector可以在每个插入上重新分配并复制所有元素,而std::list则不然) )。因此,您可以使用迭代器:

std::list< std::list<std::string>::iterator > sub_nodes;
sub_nodes.push_back( nodes.begin() );
// etc..

请注意,如果nodes使其迭代器无效(例如通过擦除元素),则sub_nodes可能包含无效条目。

答案 1 :(得分:1)

您可以使用std::reference_wrapper,它可以作为参考并可以存储在容器中。

std::list<std::reference_wrapper<std::string>> sub_nodes; // #include <functional>

请注意,尽管std::reference_wrapper对其值类型sometimes you still have to use its explicit get method具有隐式转换运算符。例如,您必须使用

(*sub_nodes.begin()).get() = "NotWelcome"; 

更改引用的对象。

以下是一个完整的工作示例:

#include <iostream>
#include <functional>
#include <list>
#include <string>

int main()
{
    std::list<std::string> nodes({"Hello","Welcome", "Hi", "World"});
    std::list<std::reference_wrapper<std::string>> sub_nodes;
    sub_nodes.push_back(*std::next(nodes.begin(), 1));
    sub_nodes.push_back(*std::next(nodes.begin(), 2));
    (*sub_nodes.begin()).get() = "NotWelcome"; 
    for (auto it = nodes.begin(); it != nodes.end(); ++it) std::cout << *it << '\t';
    std::cout << '\n';
    // Hello NotWelcome Hi World
    for (auto it = sub_nodes.begin(); it != sub_nodes.end(); ++it)
        std::cout << (*it).get() << '\t';
    std::cout << '\n';
    // NotWelcome Hi
}

答案 2 :(得分:0)

您可以创建一个字符串指针列表。

std::list<std::string*> slist1;

slist1.push_back(new std::string("Hello"));
slist1.push_back(new std::string("Welcome"));
slist1.push_back(new std::string("Hi"));
slist1.push_back(new std::string("World"));

然后使用相同的指针创建另一个列表。

auto it = slist1.begin();
std::list<std::string*> slist2(std::next(it, 1), std::next(it, 3));

slist2列表指针与slist1中的指针相同。现在,更改任何指针都会反映在两个地方。例如,以下代码将slist2中的“ Welcome”和“ Hi”替换为“ abc”。更改也反映在slist1中,因为指针相同。

for (auto &s: slist2) {
        std::cout << *s << "\n";
       *s = "abc";
 }

for (auto &s: slist1) {
        std::cout << *s << " "; //prints Hello abc abc World
    }

请注意,此代码并非异常安全,因此必须手动删除列表。为了异常安全和自动销毁,请使用智能指针。以下是使用shared_ptr的示例。

#include <iostream>
#include <memory>
#include <string>
#include <list>
int main()
{
    std::list<std::shared_ptr<std::string>> slist1;

    slist1.push_back(std::make_shared<std::string>("Hello"));
    slist1.push_back(std::make_shared<std::string>("Welcome"));
    slist1.push_back(std::make_shared<std::string>("Hi"));
    slist1.push_back(std::make_shared<std::string>("World"));

    auto it = slist1.begin();
    std::list<std::shared_ptr<std::string>> slist2(std::next(it, 1), std::next(it, 3));
    for (auto &s: slist2) {
        std::cout << *s << " ";
        *s = "abc";
    }

    for (auto &s: slist1)
        std::cout << *s << " ";  //prints Hello abc abc World
    return 0;
}