Reputation:
vector<Foo*>& getVectorOfFoo();
I want to provide a list of my Foo objects to others. Is this the best way to do it? I am returning a reference here and not copying correct?
The caller could (accidentally) modify this list right? Is there a way to avoid this possibility? Could I return const vector ? But they could always modify the Foo objects as well and not much I can do there. 10-20 different people will be writing code that use this list of Foo.
Upvotes: 5
Views: 579
Reputation: 96241
Incorporating the return type of the container into the method signature is pretty much going to prevent you from ever changing the underlying container type should an alternate container become more appropriate in the future.
You should at least consider using a typedef
to hide away the actual container type, and document the minimum capabilities of the return object rather than returning a vector
directly.
But you might consider providing an iterator interface instead, something like YourThing::const_iterator getFooBegin()
and getFooEnd()
. That way the client code can't modify the underlying container OR objects, and in fact doesn't even need to know what the container type is, allowing for greater flexibility in the future.
If you do prefer to return a container, you'd need to decide precisely what the semantics are, from the question it sounds like you want it to be read-only. In that case you would pretty much have to make a copy of the container into another vector
that would contain const pointers instead of non-const, so the client couldn't modify them. Alternately another answer provided a really good suggestion to use boost pointer containers.
Upvotes: 1
Reputation: 264381
First don't return a list of pointers.
That makes it doubly unclear about the allowed actions.
Boost has a solution (as usual).
Return a pointer container. This exposes the pointers as normal members.
boost::ptr_vector<Foo> const& getVectorOfFoo();
Now the user can not alter the returned vector.
Example:
#include <boost/ptr_container/ptr_vector.hpp>
class Foo
{
public:
void plop() {}
void poop() const {}
};
boost::ptr_vector<Foo> const& getVectorOfFoo()
{
static boost::ptr_vector<Foo> instance; // Create and fill container with FOO objects.
instance.push_back(new Foo);
return instance;
}
int main()
{
boost::ptr_vector<Foo> const& value = getVectorOfFoo();
value[0].plop(); // Fail. not a const method (comment out this line)
value[0].poop();
}
Upvotes: 9
Reputation: 69672
As already said, provide const access to the container.
But it's still not "perfect" because you need to expose the container to the world, so if you change it, you force the user code to change too if the interface isn't the same anymore.
But there is a last hope :
If you can use lambdas (C++0x), then you'd better provide a for_each algorithm :
class MyThingManager
{
public:
template< typename FunctorType > // note : could be non-template, in wich case use std::function<>
void modify_each_thing( FunctorType f ) // if you use std::function, you can put the implementation in the cpp file instead of the header/inline
{
// do some checks, or maybe generate a list of Things that you allow to modify
// then apply the function (here we assume we don't have a separate list - implementation defined for the win!)
std::for_each( m_things.begin(), m_things.end(), f ); // oh yeah
// then we can apply anything more we want
check_everything_is_still_valid();
notify_the_world();
}
// here is a simpler read-only version
template< typename FunctorType >
void for_each_thing( FunctorType f ) const { std::for_each( m_things.begin(), m_things.end(), f ); }
// in case you want the user to know how many things to manipulate
size_t things_count() const { return m_things;}
private:
std::vector<Thing> m_things; // could be any container, that's isolated from the algorithm!
};
Usage :
MyThingManager manager;
manager.for_each_thing( []( const Thing& thing ){ std::cout << "\nA thing : " << thing; } );
Upvotes: 1
Reputation: 23301
return it as const as well.
const vector<Foo *> &getVectorOfFoo();
Upvotes: 1