How can I create a list of dynamic objects in C++

I have this simple program in C++

#include <iostream>
#include <string>
#include <list>
using namespace std;

class test {
    private:
        string _name;
        list<test*> _list;
    public:
        test(const string& S): _name(S) { this->_list.clear(); }

        const string& to_string() {
            string*sp = new string("[");
            *sp += this->_name;
            for(test*tp: this->_list) {
                *sp += ",";
                *sp += tp->to_string();
            }
            *sp += "]";
            return *sp;
        }

        test& add(const string& S) {
            test*tp = new test(S);
            this->_list.push_back(tp);
            return *tp;
        }
};

int main() {
    test x("one");
    x.add("two");
    test y = x.add("three");
    y.add("four");
    cout << y.to_string() << '\n';
    cout << x.to_string() << '\n';
}

The idea is to create a list of nested lists. y is supposed to be an element of x, however when I modify y, then x is not modified.

The desired output is:

[three,[four]]
[one,[two],[three,[four]]]

but I get

[three,[four]]
[one,[two],[three]]

I can probably solve the problem by returning a pointer in test::add and modifying main:

int main() {
    test x("one");
    x.add("two");
    test*p = x.add("three");
    p->add("four");
    cout << y->to_string() << '\n';
    cout << x.to_string() << '\n';
}

However. Is there a way to use y as type test rather than p as type test*?

Upvotes: 1

Views: 2596

Answers (3)

Mr.C64
Mr.C64

Reputation: 42984

You seem to be using lots of objects dynamically allocated on the heap. Consider using smart pointers (like std::shared_ptr) instead of raw owning pointers, for proper cleanup and to avoid leaks.

I haven't spent much time on this, but taking your initial code and replacing some raw pointer usage with smart pointers (and std::list with std::vector; unless you do want list for its iterator invalidation properties, std::vector tends to be a better option), I got this code that seems to work (live sample):

Output:

[three,[four]]
[one,[two],[three,[four]]]

Source:

#include <iostream>
#include <memory>
#include <string>
#include <vector>
using namespace std;

class Test {
private:
    string _name;
    vector<shared_ptr<Test>> _list;
public:
    explicit Test(const string& S) : _name(S) { }

    string to_string() const {
        string s("[");
        s += _name;

        for (auto const& p : _list) {
            s += ",";
            s += p->to_string();
        }

        s += "]";
        return s;
    }

    shared_ptr<Test> add(const string& S) {
        auto p = make_shared<Test>(S);
        _list.push_back(p);
        return p;
    }
};

int main() {
    auto x = make_shared<Test>("one");
    x->add("two");
    auto y = x->add("three");
    y->add("four");

    cout << y->to_string() << '\n';
    cout << x->to_string() << '\n';
}

As an alternative, if it makes sense for your particular design, you may also consider returning references (T&) to your objects from test::add instead of (smart) pointers, and use unique_ptr (instead of shared_ptr) with the _list vector data member. This is a kind of alternative version (live):

#include <iostream>
#include <memory>
#include <string>
#include <vector>
using namespace std;

class Test {
private:
    string _name;
    vector<unique_ptr<Test>> _list;
public:
    explicit Test(const string& S) : _name(S) { }

    string to_string() const {
        string s("[");
        s += _name;

        for (auto const& p : _list) {
            s += ",";
            s += p->to_string();
        }

        s += "]";
        return s;
    }

    Test& add(const string& S) {
        _list.push_back(make_unique<Test>(S));
        return *(_list.back());
    }
};

int main() {
    Test x("one");
    x.add("two");
    Test& y = x.add("three");
    y.add("four");

    cout << y.to_string() << '\n';
    cout << x.to_string() << '\n';
}

Upvotes: 1

Jonathan Mee
Jonathan Mee

Reputation: 38929

Yes, you can just make y a test& to preserve the relationship with x:

Live Example

Upvotes: 1

JazzSoft
JazzSoft

Reputation: 426

You created a copy of the "three" and added "four" to it.

test y = x.add("three");

You can do like:

test& y = x.add("three");

By the way, your code creates memory leaks. Write virtual destructor.

Upvotes: 2

Related Questions