I'm having some trouble refactoring a class that uses a std::vector of unique_ptrs. I currently have a class similar to:
class DataItemA
{
// various data members
};
class DataItemB
{
// various data members
};
class PointerOwner
{
public:
PointerOwner();
~PointerOwner();
void ComplexCalculationOnItemA()
{
for (auto itr = aItemsIOwn.begin(); itr != aItemsIOwn.end(); ++itr)
{
DataItemA& itemA= (**itr);
// complex calculations that also reference DataItemB items
}
}
// methods to add / remove items from the collections
private:
// This class owns the DataItems and control their lifetime.
// The objects cannot live outside of this instance.
std::vector<std::unique_ptr<DataItemA>> aItemsIOwn;
std::vector<std::unique_ptr<DataItemB>> bItemsIOwn;
};
I was refactoring the class to extract the complex calculation to another class and was unsure how to pass the vector of unique_ptrs to the other class and clearly 'state' that the calculation does not own the pointers. Is it reasonable to:
class ComplexItemAProcessor
{
ComplexItemAProcessor(const std::vector<std::unique_ptr<DataItemA>>& itemsToProcess)
{
// can I store the itemsToProcess in a member variable
}
SomeReturnType runCalcuation() {}
private:
// store the reference to calculation
}
Or is this better which can be done:
class ComplexItemAProcessor
{
ComplexItemAProcessor()
{
}
SomeReturnType runCalcuation(const std::vector<std::unique_ptr<DataItemA>>& itemsToProcess)
{
// process the collection as per original class
}
}
The lifetime of the ComplexItemAProcessor would be limited to scope of the original method.
class PointerOwner
{
public:
PointerOwner();
~PointerOwner();
ComplexCalculationOnItemA()
{
ComplexItemAProcessor processor; /** ? pass here **/
SomeReturnType result = processor.runCalcuation(/* ? pass here */);
}
private:
// This class owns the DataItems and control their lifetime.
// The objects cannot live outside of this instance.
std::vector<std::unique_ptr<DataItemA>> aItemsIOwn;
std::vector<std::unique_ptr<DataItemB>> bItemsIOwn;
};
Is either of these better than the other? Neither feels right to me, but that could be my limited experience with smart pointers in general. Is there another way for another class to process the vector without transferring ownership?
I don't think I need shared_ptrs as PointerOwner is the only owner.
Related
I have a class that contains a private std::vector and a flag. Every time an Object inside the vector is modified I want to set that flag.
This is what I came up with..
struct Object
{
float f;
int i;
}
class SomeClass
{
private:
std::vector<Object> Data;
bool Updated;
public:
inline const std::vector<Object>& ReadData const
{
return Data;
}
inline std::vector<Object>& WriteData
{
Updated = false;
return Data;
}
}
This design is the safest I could come up with. The only issue with this is the fact I can bypass this safety mechanism when I'm using Data inside SomeClass.
So, even though I don't have lots of methods in SomeClass that operate on Data, could you suggest me a better design to achieve this? (Tracking every change to Data by setting Updated accordingly)
Your solution fails to achieve what you want.
SomeClass has an Update method that does some stuff and sets Updated to true. Data is used inside a couple of big functions, and first thing they do is to check Updated and call Update when it's false. Again this is just the best I could come up with and I always feel naked when showing my ideas to the experts here
I guess what you had in mind was something like this:
SomeClass sc;
auto& data = sc.WriteData();
sc.Update(); // checks if Updated is set to false
// if necessary Updates the data and
// resets Updated to true
sc.do_some_thing_with_data(); // this can use Data as is because Updated is true
But your "encapsulation" does not prevent a user to do this:
SomeClass sc;
auto& data = sc.WriteData(); // Updated = false
sc.Update(); // Updated = true
data[0] = 42; // modify Data
sc.do_some_thing_with_data(); // assumes data has not been modified
I suppose you actually want to call Update inside do_some_thing_with_data, however that does not change the problem:
auto& data = sc.WriteData(); // Updated = false
sc.do_some_thing_with_data(); // checks Updated, updates the data
// and resets Updated = true
// assumes data is updated -> OK
data[0] = 42;
sc.do_some_thing_with_data(); // checks Updated,
// does not update data because Updated is still true
// assumes data is updated -> WRONG
Once you returned a non-const reference you are no longer in control of modifiying the member. Your flag does not provide the "security" you expect. Returning a non-const reference is not encapsulation!
You can either not return a non-const reference, or don't pretend to encapsulate data when you don't encapusalte data (ie make the member public). If you want to be in control of modifications to data then do something along the line of:
class SomeClass
{
private:
std::vector<Object> Data;
bool Updated;
public:
const std::vector<Object>& ReadData const
{
return Data;
}
void WriteData(const Object& obj,size_t index)
{
Data[index] = obj;
Updated = false;
}
void WriteF(float f,size_t index){
Data[index].f = f;
Updated = false;
}
void WriteI(int i,size_t index){
Data[index].i = i;
Updated = false;
}
}
And if you are worried about SomeClass being able to modify Data by bypassing the setters then thats because you want SomeClass do too much. Do other stuff in a different class:
struct SomeOtherClass {
SomeClass sc;
};
Now SomeOtherClass can only modify Data in a controlled way.
PS: You seem to got confused by encapsulation vs no encapsulation. To reiterate: Returning a non-const reference is not encapsulation. If you want to encapsulate Data then make it private and do not provide direct access to it. The class that is in charge of encapsulating data has direct access to it. Other classes don't.
The only issue with this is the fact I can bypass this safety mechanism when I'm using Data inside SomeClass.
Sooooo encapsulate what you want to encapsulate, not something else. You could put your whole program in the class, and argue it would have access then to all private members. So don't, encapsulate it.
class Data {
std::vector<Object> data;
bool updated;
public:
std::vector<Object>& read() const {
return data;
}
std::vector<Object>& write() {
updated = false;
return Data;
}
};
class SomeClass {
Data data;
public:
const std::vector<Object>& readData() const {
return data.read();
}
std::vector<Object>& writeData() {
return data.write();
}
};
How can I get the access to a private vector outside the class? I want to modify parameters of this objects.
I try to make getter and return the vector by reference, but when I try to change parameters of objects included in vector in main functions changes in vector are not saved.
class Restaurant
{
std::vector <Waiter> waiters_vector_;
public:
inline std::vector<Waiter> &GetWaitersVector() { return waiters_vector_; }
void Restaurant::AddWaiter(Waiter tmp)
{
waiters_vector_.push_back(tmp);
}
Restaurant();
~Restaurant();
};
class Waiter
{
int current_group_id_=0;
public:
int GetCurrentGroupId()
{
return current_group_id_;
}
void SetCurrentGroupId(int tmp)
{
current_group_id_ = tmp;
}
Waiter();
~Waiter();
};
int main()
{
Restaurant restaurant1;
Waiter w1, w2, w3;
restaurant1.AddWaiter(w1);
restaurant1.AddWaiter(w2);
restaurant1.AddWaiter(w3);
for (Waiter element : restaurant1.GetWaitersVector())
{
element.SetCurrentGroupId(123);
}
for (Waiter element : restaurant1.GetWaitersVector())
{
std::cout << element.GetCurrentGroupId() << std::endl;
}
}
result:
0
0
0
Both of your for loops are making copies
for (Waiter element : restaurant1.GetWaitersVector())
you want to modify references to the actual objects
for (Waiter& element : restaurant1.GetWaitersVector())
for (Waiter element : restaurant1.GetWaitersVector()) operates with a copy of the vector.
If you want to operate on the reference use
for (auto& element : restaurant1.GetWaitersVector())
// ^^^^^
instead.
But besides what's mentioned above, exposing your interned vector is a bad design idea. You should rather have a getter that does
inline const std::vector<Waiter> &GetWaitersVector() const { return waiters_vector_; }
and thus force clients accessing it to use specific functions of your class like AddWaiter() to modify it.
How can I get the access to a private vector outside the class? I want to modify parameters of this objects.
You don't. Or rather, you need to decide: Is the vector of waiters something private, or isn't it? Is it an implementation detail that code using this class should not be aware of? If not, do you really want to be able to manipulate it as-is on the outside? Or perhaps you want to use the PIMPL idiom to provide a .waiters() method which returns an obscure class, with methods such as add(...), `remove(...), etc.?
Those are design decisions for you to make.
I have simplified the code as much as possible.
So I have two class:
class EntityManager
{
public:
std::shared_ptr<std::vector<Entity> > getEntities()
{
return std::make_shared<std::vector<Entity> >(m_entities);
}
private:
std::vector<Entity> m_entities{};
};
and
class System
{
public:
void loadEntities(std::shared_ptr<std::vector<Entity> > entities)
{
m_entities = entities;
}
private:
std::shared_ptr<std::vector<Entity> > m_entities;
};
Now basically I want the m_entities of System to point to the m_entities of EntityManager.
I did this:
system = System();
system.loadEntities(m_entityManager.getEntities());
But then I pushed back an element into the m_entities vector of EntityManager and this element wasn't added in the m_entities vector of System, which means my pointer doesn't point.
Where is my mistake?
Thanks!
Your problem is this line: return std::make_shared<std::vector<Entity> >(m_entities);
What is happening is that the shared_ptr manages a new std::vectory<Entity> container which is initialized as a copy of m_entities. Therefore, modifying the instance in the shared_ptr doesn't modify the data member in the EntityManager class and of course the shared_ptr won't see changes made to EntityManager::m_entities.
std::make_shared doesn't "make this thing shared"; it "makes a thing that will be shared".
So, you can't just make a shared pointer out of nowhere that points to something that already exists.
Your code dynamically allocates a std::vector, copy constructed from m_entities and managed by a std::shared_ptr. It's shorthand for this:
std::vector<Entity>* ptr_to_copy = new std::vector<Entity>(m_entities);
return std::shared_ptr(ptr_to_copy);
It's not clear what you're trying to do, from the code that (by your own admission) does not achieve that goal. But it seems unlikely that std::shared_ptr is appropriate here.
If it is, then make the vector dynamically-allocated and shared from the start; otherwise, just return a reference to the vector as it is.
Hack example of a pointer-free solution.
#include <string>
#include <iostream>
#include <vector>
//Hack-sample Entity class
class Entity
{
public:
Entity(const char * name): m_name(name)
{
}
void print() // this is stupid in real life. Prefer a << overload
{
std::cout << "Hi! I'm " << m_name << "!\n";
}
private:
std::string m_name;
};
class EntityManager
{
private:
std::vector<Entity> m_entities;
public:
// hide the fact that a vector is being used to store the entities.
// you can now swap out the vector for most standard containers without
// changing any code other than the using and the declaration of m_entities
using iterator = std::vector<Entity>::iterator;
EntityManager(): m_entities({"bob", "bill"})
// just pre-loading a few test entities
{
// RAII says you should load the entities from their source here
}
// get the first entity.
iterator begin()
{
return m_entities.begin();
}
// get the end of the entity list
iterator end()
{
return m_entities.end();
}
// adds an entity
void addEntity(const Entity & entity)
{
m_entities.push_back(entity);
}
// removes an entity
iterator removeEntity(iterator rem)
{
return m_entities.erase(rem);
}
};
class System
{
public:
// example method to show System working with EntityManager by printing all of the Entities
void printEntities()
{
for (EntityManager::iterator it = m_entityManager.begin();
it != m_entityManager.end();
++it)
{
it->print();
}
}
// example method to show System working with EntityManager by adding Entities
void addMoreEntities()
{
m_entityManager.addEntity(Entity("Ted \"Theodore\" Logan"));
m_entityManager.addEntity(Entity("Excellent!!!"));
}
private:
EntityManager m_entityManager ;
};
// sample test
int main()
{
System test;
test.printEntities();
test.addMoreEntities();
test.printEntities();
}
THIS HAS BEEN A HACK. THIS HAS ONLY BEEN A HACK.
If you want to do EntityManager right, see Writing your own STL Container for hints. If you want all of the bells and whistles, the job is fairly complicated. Depending on how you are using EntityManager and the complexity of the Entity management logic, you may be better off discarding EntityManager and just using the plain, old std::vector.
Addendum: What is meant by Resource Acquisition is Initialization (RAII)?
In my class implementation, I have something like this:
base class
class swcWidget :
public swcRectangle
{
public:
swcWidget();
virtual ~swcWidget();
void update(float dt);
protected:
inline virtual void oPaintOnTop() { }
private:
};
derived class
class swcButton :
public swcWidget
,public swcText
{
public:
swcButton();
virtual ~swcButton();
static const int DEFAULT_SIZE = 20;
protected:
private:
void oPaintOnTop();
};
class swcApplication
{
public:
swcApplication(int argc, char *argv[]);
virtual ~swcApplication();
int run();
struct Controls
{
typedef std::vector<swcWidget*> vWidgets; //option 1
~Controls();
/////////////////////////////////
// M A I N P R O B L E M //
/////////////////////////////////
void add(swcWidget &&widgets); //most preferred option
//but gets demoted to base class.
void add(swcWidget *widgets); //second choice
//but should I make a copy of it?
//or just make a reference to it?
//and this one does what I wanted to.
//but still unsure on other things I don't know
void add(swcWidget *&&widgets); //this compiles fine (?)
//I don't know what kind of disaster I can make into this, but still does not do what I wanted.
inline vWidgets &getWidgets() {
return widgets;
}
private:
vWidgets widgets;
};
Controls controls;
};
I know some working option like this:
making the
swcApplication::Controls::widgets
as type of
std::vector<std::shared_ptr<swcWidget>>
but my code will bind into std::shared_ptr and I cannot make simple syntax like this:
swcButton btn;
app.controls.add(std::move(btn));
Example usage:
main.cpp
int main(int argc, char *argv[])
{
swcApplication app(argc, argv);
app.windows.create(640, 480);
if (font->load("fonts\\georgia.fnt") != BMfont_Status::BMF_NO_ERROR)
{
puts("failed to load \"georgia.fnt\"");
}
{
swcButton btn;
btn.setPosition(100, 100);
btn.setFont(font);
btn.text = "Ey!";
app.controls.add(std::move(&btn));
// btn.text = "Oy!";
}
return app.run();
}
Update:
Here's the temporary definition of swcApplication::Controls::add() although it may still vary
void swcApplication::Controls::add(swcWidget &&widget)
{
widgets.push_back(std::move(widget));
}
If a class is moveable, then it will in turn move it's members one by one. For this to be efficient, these members must either be small POD's or must be allocated on the heap. You must add this functionality, not forget to move any member, and object slicing is a concern to watch out for.
Given the class is non-trivial, you have the most efficient move construct available when you just use a pointer directly (at the cost of heap allocation time of course). No slicing is possible, and no member can be forgotten to be moved, since you move the whole object in one go. The one hurdle to watch out for is to keep track of who owns the pointers - you'd better set it in stone, but if that's done then there are no issues anymore.
The move semantics are wonderful, but if your classes are somewhat involved I think pointers in this case are easier / more efficient to work with. I'd thus stick with the pointer variant, and make sure your collection will own the pointers (and release them again via RAII) - make liberal use of comment in your public interface saying so. You can do this by storing some form of smart pointer (hint: be careful with unique_ptr's!), or (less safe) make and always use a Clear() member that delete's all pointers before clear()'ing the collection.
EDIT
Whet you define your widgets member to be of type vector, then example code could be:
To class swcApplication add:
void swcApplication::Controls::ClearWidgets() {
for (auto& nextWidget: widgets) {
delete nextWidget;
}
widgets.clear();
}
Don't forget to call ClearWidgets at the appropriate times (like in your destructor).
Adding widgets can be done with:
// Note: any passed widget will not be owned by you anymore!
template <typename Widget>
void swcApplication::Controls::add(Widget*& widget) {
widgets.push_back(widget);
widget = nullptr;
}
From now on you can add widgets like
swcButton* btn = new swcButton;
app.controls.add(btn);
// btn is now owned by app.controls, and should be set
// to nullptr for you to prevent misuse like deleting it
Using a smart pointer here should make it more safe, though storing unique_ptr's makes accessing them a bit error-prone (watch out for grabbing ownership back from the container when accessing them), and a shared_ptr gives overhead which might be unneeded here.
I think I have a design issue here and I would really appreciate your help.
I have a class Base representing a basic algorithm.
class BaseAlgo: public Algo<double>
{
public:
/// data structures
// ...
//
struct Item {
double profit;
double weight;
double xjSolution;
};
typedef std::pair<double, std::vector<Item>::iterator> ScaledItem;
protected:
std::vector<Item> & items_;
boost::ptr_vector<ScaledItem> largeItems_;
}
The BaseAlgo has some functions, some of them virtual, others not.
As a derived class I have
class DerivedAlgo: public BaseAlgo
{
public:
/// enhanced data structures
// ...
//
struct DerivedScaledItem : ScaledItem {
int additional;
};
}
In my virtual functions which I overload in DerivedAlgo, I need access to the additional parameter of DerivedScaledItem which is not quite the original intent of polymorphism. Is it somehow possible or do you propose a different design approach? I am open to anything at the moment as I am completely stuck.
Right now, the largeItems_ member ptr_vector in BaseAlgo holds ScaledItems (internally as pointers). I thought, I could use this somehow like this:
// in DerivedAlgo
void someMethod(std::vector<Item>::iterator someiterator){
DerivedScaledItem doubledItem = {};
doubledItem.first = 4.5;
doubledItem.second = someiterator;
doubledItem.additional= 2;
largeItems_.push_back(new UnboundedScaledItem(doubledItem));
boost::ptr_vector<DerivedScaledItem>::iterator it = largeItems_.begin();
std::cout << "added large item " << *it << std::endl;
}
When I cout the just added object, additional is set to 2. But after that, calling the getter for largeItems_, the additional field will be set back to 0, only the two fields which are known in ScaledItem are then set.
// in BaseAlgo
const boost::ptr_vector<ScaledItem>& getLargeItems() const
{
return largeItems_;
}
// from my test.cpp
DerivedAlgo obj;
// ... define someiterator
obj.someMethod(someiterator);
boost::ptr_vector<BaseAlgo::ScaledItem> largeItems = knapsack.getLargeItems();
boost::ptr_vector<DerivedAlgo::DerivedScaledItem>::iterator it = largeItems.begin();
std::cout << "read large item " << *it << std::endl;
I guess you didn't tell boost how to clone your ptr_vector-s elements, like described here:
http://www.boost.org/doc/libs/1_54_0/libs/ptr_container/doc/tutorial.html#cloneability
So in this line, where you create a copy of the vector (you could avoid this by declaring largeItems as a reference), they get copied via the constructor of ScaledItem, which looses your additional member.
boost::ptr_vector<BaseAlgo::ScaledItem> largeItems = knapsack.getLargeItems();
Regarding your question about another design:
You could pass the type of the vectors elements as a template parameter to the base class.
You could move the vector into the derived class, and provide only (virtual, abstract) functions to access single elements in the base class. If the base class shall also be able to create elements, you may need some kind of factory method. Because you don't want the base kind of elements in the vector.