It's fine to use pointers as members, but in your case you are simply working around a minor hiccup that really doesn't warrant the use of pointers, and using pointers can be dangerous as evidenced by an issue I'll point out shortly.
As is, it does not work as I have not defined a default constructor for Foo.
This is easily resolved by using initializers for Bar:
Bar(const Foo &f1, const Foo &f2) : foo1(f1), foo2(f2), foo3(f1.add(f2)) {}
as demonstrated here:
#include <iostream>
class Foo {
public:
double m_foo;
Foo(double foo) : m_foo(foo) {}
Foo add(Foo f) { f.m_foo += m_foo; return f; } // returns temporary!
};
class Bar {
public:
Foo m_foo1;
Foo m_foo2;
Foo m_foo3;
Bar(const Foo &foo1, const Foo &foo2);
};
Bar::Bar(const Foo &foo1, const Foo &foo2)
: m_foo1(foo1)
, m_foo2(foo2)
, m_foo3(m_foo1.add(m_foo2))
{
}
int main() {
Foo foo1(20.0);
Foo foo2(22.0);
Bar bar(foo1, foo2);
std::cout << bar.m_foo3.m_foo << "\n";
return 0;
}
Live demo: http://ideone.com/iaNzJv
In your pointer solution you introduce a glaring pointer problem: a pointer to a temporary.
foo3 = &(f1.add(f2));
f1.add returns a temporary Foo, which you take the address of, and then it goes away. This is a dangling pointer.
Your pointer implementation also doesn't explicitly take pointers as its inputs so f1 and f2 could run into the same problem:
Bar(Foo(20), Foo(22)); // two temporary Foos passed by reference
// but having their addresses taken. ouch.
If you're taking pointers, it's best to do that at the api to your class; you're going to have to care about the lifetime of the things pointed to, and try to make it easier for a caller to tell that you are doing so.
Bar(Foo* f1, Foo* f2);
But now if you're going to have F3 you're going to be responsible for managing it's memory:
Bar(Foo* f1, Foo* f2)
: foo1(f1), foo2(f3), foo3(new Foo(*f1.add(*f2)))
{}
~Bar()
{
delete f3;
}
So in your example case, using members is probably drastically better.
Save the use of pointers for large objects that you definitely don't want to copy, and where you can't use a move operation.
--- EDIT ---
The problem of conveying ownership of pointers has been largely solved in modern C++ (C++11 and higher), through "smart pointers", in particular std::unique_ptr
and std::shared_ptr
.
It is generally considered Best Practice to use these instead of raw pointers, although it requires learning some newer C++ concepts.
#include <memory>
struct Foo {};
class Bar {
public:
std::unique_ptr<Foo> m_f1; // we will own this
std::unique_ptr<Foo> m_f2; // and this
Bar(std::unique_ptr<Foo> f1) // caller must pass ownership
: m_f1(std::move(f1)) // assume ownership
, m_f2(std::make_unique<Foo>()) // create a new object
{}
~Bar()
{
// nothing to do here
}
};
int main() {
auto f = std::make_unique<Foo>();
Bar(std::move(f)); // the 'move' emphasizes that
// we're giving you ownership
// 'f' is now invalid.
return 0;
}
Live demo: http://ideone.com/9BtGkn
The elegance of this is that when Bar goes out of scope, the unique_ptr
s will ensure that the objects they own are destroyed for us -- we don't have to remember to delete
them.
In the above example, it would probably have been much better to make m_f2
a member rather than a pointer.