Program design with polymorphism and differing data structures - c++

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.

Related

How can I modify a vector from outside the class

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.

Vector of shared pointers updates existing elements on push_back

Basically, I have a vector of shared_ptr as member data to a class.
This vector is populated by calling a member function from another class function.
Problem new elements added to the vector updates values of existing elements.
The setup is as follows:
class A
/*
* Class A has a header with class declarations and a distinct implementation.
* For the sake of brevity, the relevant information is shown in this snippet.
*/
class A {
public:
A(std::string name) : m_name(name);
std::string& get_name();
private:
std::string m_name;
}
typedef std::shared_ptr<A> A_ptr;
class B
/*
* Class B has a header with class declarations and a distinct implementation.
* For the sake of brevity, the relevant information is shown in this snippet.
*/
class B {
public:
void add_element(A_ptr& element) {
/*
this is where the problem is:
i DO get the proper element (i can verify this by calling the get_name() method on `element`).
Assume we got in succession:
- ONE
- TWO
- THREE
...
*/
std::cout << element -> get_name << std::endl;
m_elements.push_back(element);
/*
...
fast forward here, when I print the content of the vector held in this class,
the old elements added have been updated somehow and I get this:
- when we get ONE, the vector contains: [ONE]
- when we get TWO on the next call to this function, the vector contains: [TWO, TWO]. I expect [ONE,TWO]
- when we get THREE on the next call to this function, the vector contains: [THREE, THREE, THREE]. I expect [ONE, TWO, THREE]
-> WHAT ON EARTH IS GOING ON HERE?
*/
for (auto it = m_elements.begin(); it != m_elements.end(); ++it)
std::cout << (* it) -> get_name() << std::endl;
}
private:
std::vector<A_ptr> m_elements;
}
typedef std::shared_ptr<B> B_ptr;
class B is the problem class. In the add_element() function, I am getting the proper elements to add (namely ONE, TWO and THREE).
But, after the push_back(), not only the new element is added, the existing elements all have their values updated to the new element that got added.
So say I add elements with names ONE then TWO, instead of getting a vector with elements as [ONE, TWO], I get [TWO, TWO].
class C
/*
* Class C has a header with class declarations and a distinct implementation.
* For the sake of brevity, the relevant information is shown in this snippet.
*/
class C {
public:
void do_something() {
std::string name = name_obtained_dynamically();
B_ptr manager(new B());
do {
A_ptr element_one(nullptr);
A_ptr element_two(nullptr);
if (we_are_good) {
element_one.reset(new A(name));
}
else {
element_two.reset(new A(name));
}
if (element_one !=nullptr)
manager -> add_element(element_one);
else
manager -> add_element(element_two);
} while(condition_is_true);
}
}
class C calls add_element() from class B and keeps adding elements to said vector.
So far as I can tell, the problem probably lies with my use of shared pointers inside that loop.
But what exactly I am doing wrong, I cannot tell.

Auto return type of a function in c++

I have a problem that Im not able to solve. I have tried to find similar question here, but didnt find working solution for me.
My structure is:
class Base
{
unsigned int ID;
};
class Position: public Base
{
float x,y;
Position(float a, float b): x(a), y(b) {}
}
class Mass: public Base
{
float mass;
Mass(float a): mass(a) {}
}
I store pointers to all attributes in a map accesed with strings.
I would like to have a function, that can return any attribute from this list using names.
Structure and desired functionality should be like this:
std::map<string, Base*> attributes;
???? getAtt(string name)
{
return attributes[name];
}
Position pos(1,2);
Mass mass(25.6);
attributes.emplace("TEST_POSITION", &pos);
attributes.emplace("TEST_MASS") &mass);
cout << "Mass of this object is " <<getAtt("TEST_MASS").mass << endl;
cout << "X - Position of this object is " << getAtt("TEST_POSITION").x ;
PRINTS: Mass of this object is 25.6
X - Position of this object is 1
This function, addition of attributes and memory managment should be encapsuled in another class, but I think that wont be such problem after i get this thing solved.
So is there a way to do that? I was thinking about templates, but I dont understand them enough to make them work :( I was thinking about not storing all attributes in one array, but this way it is really easy.
Thanks for any suggestions :)
Your getAtt will return a Base*, like this:
Base* getAtt(const string& name)
{
...
}
But Base class doesn't provide a interface for all its derived class, so you can't just do
getAtt("TEST_MASS").mass
instead you have to do this:
dynamic_cast<Mass*>(getAtt("TEST_MASS"))->mass;
dynamic_cast<Position*>(getAtt("TEST_POSITION"))->x;
There are alternatives, for example you can use a tagged union, but that might be too complex for your problem.
By the way, [] operator of map will create a element if it doesn't exist, so you need to check getAtt isn't returning a nullptr.
The compiler cannot deduce the return type without you hinting it. You can use a template for that:
template <typename T>
T& getAtt(string name)
{
return dynamic_cast<T&>(*attributes.at(name));
}
Edit: use at instead of [], [] has the side-effect that it creates non-existing keys.
And then call it like this:
getAtt<Mass>("TEST_MASS").mass
getAtt<Position>("TEST_POSITION").x
However, this code would be nightmare to debug. Try to avoid generic attributes and use strong-typed variables, ie. instead of:
std::map<string, Base*> attributes;
attributes.emplace("mass", Mass(…));
attributes.emplace("position", Position(…));
use:
Mass mass;
Position position;
You can return a reference object that has a conversion operator to the type. If the conversion is implemented with a dynamic cast, the result will be NULL if an attempt is made to assign the object to something that it is not. Dynamic cast requires Base to have a virtual method (you could just add a virtual destructor).
class Base {
unsigned int ID;
protected:
virtual ~Base () {}
};
class Ref {
friend Ref getAtt (std::string name);
Base *ref_;
Ref (Base *p = 0) : ref_(p) {}
public:
template <typename D> operator D * () const {
return dynamic_cast<D *>(ref_);
}
};
Ref getAtt (std::string name) { return attributes[name]; }
This technique does not allow you to treat a Ref as any particular type. It allows you to assign it to something that it is allowed to become.
Mass *m = getAtt("TEST_MASS");
Position *p = getAtt("TEST_POSITION");
cout << "Mass of this object is " << m->mass << endl;
cout << "X - Position of this object is " << p->x ;
If the resulting pointer is NULL, it means either the item did not exist, or you are attempting to assign it to the wrong thing.
Position *p = getAtt("TEST_MASS");
assert(p == NULL);

Inheriting from std:: vector

I am currently creating a class that has to be derived from std:: vector. I realize its probably bad to do this but I'm required to. Now my question is how do you access the created vector in the member functions to basically make the class access itself like a regular vector of integers? For example I am looking for the equivalent of myVector.at(0) to return the first term in the vector. Also, the size of the vector should always be 6. Here is the code I have so far:
class aHistogram : public vector<int>
{
public:
aHistogram(); //default constructor for histogram class
void update(int face); //Function to update histogram
void display(int maxLengthOfLine); //Displays histogram to the scale of maxLengthOfLine using x's
void clear();//Function to clear histogram bin counts
int count(int face) const; // Function to return number of times a face has appeared
private:
int numx, m, j; //Variables used in functions
};
#endif
The function that requires the class to access itself is below, I know there is no vector called "myVector" but what I'm lost about is the equivalent syntax to be able to perform the operation.
void aHistogram::clear()
{
//Clears bin counts to 0
myVector.at(0) = 0;
myVector.at(1) = 0;
myVector.at(2) = 0;
myVector.at(3) = 0;
myVector.at(4) = 0;
myVector.at(5) = 0;
}
If the function in question isn't overridden in the derived class, you
can just call it:
void HistoGram::clear()
{
at( 0 ) = 0;
// ...
}
This is also true for operators, but you'll have to use (*this) as the
left hand operator:
void HistoGram::clear()
{
(*this)[0] = 0;
// ...
}
If the function or operator is overridden, you'll either have to
qualify the function name,
void HistoGram::clear()
{
std::vector<int>::at( 0 ) = 0;
// ...
}
or cast the this pointer to the base class type:
void HistoGram::clear()
{
(*static_cast<std::vector<int>*>( this ))[0] = 0;
// ...
}
But are you sure that you want public inheritance here? You state that
the size of the vector should always be 6. There's no way you can
guarantee that using public inheritance; at the least, you need private
inheritance, and then using declarations for the operations that you
want to support. (I've a couple of cases where I've needed restricted
std::vector like this, which I've implemented using private
inheritance. And sometimes forwarding functions, when for example
I've wanted to expose only the const version of the function.)
Also: there are very, very few cases where std::vector<>::at is
appropriate. Are you sure you don't want [], with the bounds checking
you get in most modern implementations.
Instead of deriving from std::vector, in this case contain one (as a data member).
The problem with deriving is that it's then possible to treat a Histogram instance as just a std::vector, doing things that invalidate assumptions about the values of added data members.
In more technical jargon, with class derivation you have no guaranteed class invariant above the one provided by std::vector.
As a general rule of thumb, think of data member before class inheritance.
Sometimes inheritance is the thing, even inheritance from standard library container classes (e.g., std::stack is designed for inheritance), but not in this case.
About this: the size of the vector should always be 6.
You probably want to forbid some functionality to the user of the class. For example
vector::push_back
vector::pop_back
vector::insert
are functionalities that can change the size of the vector.
You can achive this by making such functions private members in the child class:
class aHistogram : public vector<int>
{
public:
aHistogram(){};
private:
vector<int>::push_back;
vector<int>::pop_back;
vector<int>::insert;
int numx, m, j;
};

Using a std::vector<std::unique_ptr>> to another class / function

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.