Reputation: 2668
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
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
Reputation: 38929
Yes, you can just make y
a test&
to preserve the relationship with x
:
Upvotes: 1
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