Passing data member by reference - c++

If I have a class like so (forgive the rough syntax):
class Z{
public:
void method1();
void method2(vector<X>& v);
void method3(vector<X>& v);
private:
std::vector<X> myvec;
}
and basically I want to pass myvec from method1() through method2() to method3() where it will be modified. If my method1() looks like this:
void Z::method1(){
vector<X> v = z::myvec; //What do I have to do with this line so that the changes
method2(v); //made to v in method3() are permanent and not lost once
} //method1() returns?
void Z::method2(vector<X>& v){
method3(v);
}
//Modify vector<X> myvec
void Z::method3(vector<X>& v){
v.push_back(<something>);
}
How could I change the vector in method3() so that once method1() returns, the changes made to the vector are permanent and not lost?

Just make v a reference:
vector<X>& v = myvec;
or simply pass myvec itself:
method2(myvec);

Why not just allow method3 to use the class' member data directly? That's a perfectly normal thing to do.
void Z::method3(){
myvec.push_back(<something>);
}
But if you really want to do it your way (and I would advise against it as it's so idiosyncratic), replace your first commented line with
vector<X>& v = z::myvec;

I dont know why you exactly wanted this flow, method1()-->method2()-->method3(). If this is something that is not intentional you can directly use data member myvec in method3(). But I am sure this could not be the case, there must be a use case because of which you are using it.
for the other way, you can modify, method1() in this way.
void Z::method1(){
method2(z::myvec);
}
and keep rest of the function as it is.
Actually what's happening in your case is in your method1() you are actually creating new object and passing that new object with reference so all that changes is being made to that new vector not the original vector.
vector<X> v = z::myvec; // this creates new object.
You test is with debugging.
please let me know if this answers your doubt.

Related

Passing a map between classes

This is in relation to an earlier question I had. I haven't managed to solve the problem there but for now I'm just trying to get better acquainted with the code to figure out how to deal with that problem.
Towards that goal, I've got around to trying out the suggestions given in that question and I'm a little stumped as to why the following isn't working.
in the header I have
class A {
public:
typedef std::multimap<int, double> intdoublemap_t;
const intdoublemap_t& getMap() const;
void setkey(int k);
void setvalue(double v);
void insertIntoMap();
intdoublemap_t mMapA;
private:
int key;
double value;
};
class B {
public:
typedef std::multimap<int, double> intdoublemap_t;
void mapValues(const A& a);
private:
intdoublemap_t mMapB;
};
in the implementation I have
const A::intdoublemap_t& A::getMap() const { return mMapA; }
void A::setkey(int k) { key = k; }
void A::setvalue(double v) { value = v; }
void A::insertIntoMap(){mMapA.insert(std::make_pair(key, value));}
void B::mapValues(const A & a){ const A::intdoublemap_t& mref = a.getMap();
mMapB = mref; }
and in main()
A a;
a.setkey(10);
a.setvalue(1232.2);
a.insertIntoMap();
B b;
b.mapValues(a);
The code compiles fine and everything to do with a works as expected but the map is not passing to b at all. It stays empty
Can anyone tell me why?
edit: I took another look at this and saw how to do it. I knew it was something stupidly basic. I just had to set mref in the function to a map in B and then could call a function to work on that map within B.
As #FrancoisAndrieux notes, your getMap() only sets a reference local to the function - not the class' intdoublemap_t mref. If you want the latter to be a reference to a map elsewhere, you have three options:
Make it intdoublemap_t& mref, initialize it on construction of the B instance.
Make it std::reference_wrapper<intdoublemap_t> mref, set it whenever you want (e.g. in mapValues().
Make it intdoublemap_t* (or std::shared_ptr<intdoublemap_t> in both A and B), set it whenever you like.
Note: As #FrancoisAndrieux says in a comment, with the second and third option (and without std::shared_ptr) you will have to be careful not to allow the reference to be used after the original object's lifetime has expired.
Having said all the above - I must also say that your design seems rather off to me. You should really not be doing any of these things and I'm 99% sure you're approaching your task the wrong way.

Copy constructors and passing classes around

Reverting this back to the original question. Going to post the more detailed question as a new question. Thanks everyone for the help and advice of avoiding the new. Having trouble passing around instances of objects in this question.
class dogClass {
public:
void setVariableA(std::vector<double> newNum) {
variableA.push_back(newNum);
}
dogClass &dogClass::operator=(const dogClass &src) {
variableA = src.variableA;
return *this;
}
private:
std::vector<std::vector<double>> variableA;
};
class animalClass {
public:
void getDogOne(dogClass &dogOne) {
dogOne = dogs[0];
}
void setDogOne(dogClass dogOne) {
dogs.push_back(dogOne);
}
private:
std::vector<dogClass> dogs;
};
int main() {
animalClass* iAnimalClass = new animalClass();
dogClass* iDogClassOne= new dogClass();
iAnimalClass->setDogOne(iDogClassOne);
std::vector<double> newNum;
newNum.push_back(12);
newNum.push_back(15);
iDogClassOne->setVariableA(newNum);
dogClass iDogClassTwo;
iAnimalClass->getDogOne(iDogClassTwo);
//Why are iDogClassOne and iDogClassTwo not equal.
return 0;
}
There are a couple of issues.
you don't have a copy constructor for your dog class as you have indicated in your title. what you have done is overloaded the = operator.
you are trying to pass iDogClassOne as pointer to the animalClass:: setDogOne() but animalclass::setDogOne receives the arguments by value, not as reference or pointer.
you can use the dereference operator*iDogClassOne. this pretty much telling the compiler you want to pass the object which the iDogClassOne is pointing at.
dogClass* iDogClassOne = new dogClass();
iAnimalClass->setDogOne(*iDogClassOne);
or a better way of doing, you can completly remove the heap allocation
dogClass dogobj;
iAnimalClass->setDogOne(dogobj);
same problem as described in (2). but this time your animalclass::getDogOne receives the arguments by a reference.this again can be easly fixed by modifying the getdogone or/and by passing a pointer. i will leave this as it is. if you still need a help let me know in comments and will edit it further. but i encourage you to try to give it a go first.
you are missing semicolon (;) at the end of class declarations

c++ move semantics where allegedly there shouldn't be benefit

I needed to use some class today that followed this basic design:
class Task {
public:
Task() {
Handler::instance().add(this);
}
virtual void doSomething() = 0;
};
class Handler {
std::vector<Task*> vec;
//yea yea, we are locking the option to use default constructor etc
public:
static Handler& instance() {
static Handler handler;
return handler;
}
void add(Task* task) {
vec.push_back(task);
}
void handle() {
for (auto t : vec) {
t->doSomething();
}
}
};
template <class T, int SIZE>
class MyTask : public Task {
T data[SIZE];
public:
virtual void doSomething() {
// actually do something
}
};
//somewhere in the code:
Handler::instance().handle();
now, my class is something like
class A {
MyTask<bool, 128> myTask;
public:
A(int i) {}
};
the way I wanted to do it is having a map where instances of A are values
static std::map<int, A> map = {
{42, A(1948)},
{88, A(-17)}
};
first to clarify something - this code needs to run on a real time embedded system so I'm not allowed to allocate memory using new for several legacy reasons.
My problem was that the actual objects in the map weren't the ones I explicitly created and so they didn't register in the Handler class (so I didn't get the benefit of the Handler::handle calls).
I tried figuring a nice way to solve this without doing something ugly like first creating an array of A then only point to these objects in the map for example.
I never used move semantics before but I've read little bit about them and thought they can be my solution.
however, after reading this answer (specificaly the very first example) it seemed that I can't really benefit anything from using move semantics.
I tried it anyway (coz why the heck not...) and did something like this instead:
static std::map<int, A> map = {
{42, std::move(A(1948))},
{88, std::move(A(-17))}
};
now for my surprise the copy constructor of MyTask was still called (I put print in it to verify) but for some reason now the handler registration worked well and my instances enjoyed the doSomething() calls.
I tried reading more thoroughly about std::move to understand what exactly happened there but couldn't find the answer.
can anyone explain it? does std::move moves the this pointer somehow? or maybe it just caused the registration to happen correctly somehow and had nothing real to do with the moving attempt
thanks
edit:
to further clarify what I'm asking:
I understand the use of std::move was not contributing to what's being done there.
But for some reason it did get my objects in the map to get the doSomething() calls through the handler. I'm looking for that reason
on a side note as it probably belongs to a different question - is there any decent way to initialize a map this way without the overhead of creating each object twice?
Your question has a lot more in it than it needs to but I think I understand the root question here. The std::map constructor receives an initialization_list, you're calling (5) from this list. Objects are copied out of an initializer_list when iterating over it rather than moved because a copy of an initializer_list doesn't copy the underlying objects. The same affects other std containers, here is an example with vector to demonstrate. (live link)
#include <vector>
#include <iostream>
struct Printer {
Printer() { std::cout << "default ctor\n"; }
Printer(const Printer&) { std::cout << "copy\n"; }
Printer(Printer&&) { std::cout << "move\n"; }
};
int main() {
std::vector<Printer> v = {Printer{}};
}
if you use {std::move(Printer{})} you'll add another move into the mix that the compiler can't easily get optimize away.

c++ generic storage and retrieval of objects using string handle

What would be the elegant and simple way (if exists) to implement a storage of generic objects (all other objects inherit from base). Once the object is stored, use a string handle to retrieve object or copy into another.
class Object{
public:
Object(){};
~Object(){};
};
class ObjectHandler{
public:
ObjectHandler(){};
~ObjectHandler(){};
void InsertObject(std::string handle, std::shared_ptr<Object> obj){
// some things happen before inserting
_obj.insert(std::make_pair(handle,obj));
}
std::shared_ptr<Object> RetrieveObject(std::string handle){
// some things happen before retrieving
return _obj[handle];
}
private:
std::map<std::string,std::shared_ptr<Object>> _obj;
}
For example, user defined classes are
class Dog : public Object{
public:
Dog(){};
Dog(std::string name){dogName=name};
~Dog(){};
std::string dogName;
//...
}
class Cat : public Object{
public:
Cat(){};
Cat(std::string name){catName=name};
~Cat(){};
std::string catName;
//...
}
And the following code is executed
void main(){
ObjectHandler oh;
Cat c("kitten"), cc;
Dog d("doggy"), dd;
oh.InsertObject("cat#1",c);
oh.InsertObject("dog#1",d);
cc = oh.RetrieveObject("cat#1");
dd = oh.RetrieveObject("dog#1");
std::cout << cc.catName << std::endl; // expect to print 'kitten'
std::cout << dd.dogName << std::endl; // expect to print 'doggy'
}
I believe there should be some well established idea (pattern) to make this working right.
I also suspect std::shared_ptr might be useful here.
Thanks,
I would exercise caution here, in your example you're storing your objects as Object strictly (on the stack), since that would only allocate enough space for something of type Object, should you insert something that inherits from the type, it would have the part that describes the subclass sliced.
Good examples of the problem at hand:
http://www.geeksforgeeks.org/object-slicing-in-c/
What is object slicing?
One way to get around the problem is to handle pointers to the objects in your ObjectHandler instead, the objects themselves allocted on the heap.
If I'm just misinterpreting your post, then I apologise.
But if you as you said, will store smart pointers to the object instead, making a pair should look something like this:
std::map<std::string,std::shared_ptr<Object>> _obj;;
std::string handle = "hello"; //Or whatever the actual handle is.
std::shared_ptr<Object> keyvalue(new Object());
objects[handle] = std::shared_ptr<Object>(keyvalue); //alternative to std::make_pair
objects.insert(std::make_pair(handle, std::shared_ptr<Object>(keyvalue))); //this also works
Depending on at what point you want to start handling objects with smart pointers, insertion might look like:
void InsertObject(std::string handle, Object* obj){
_obj.insert(std::make_pair(handle,std::shared_ptr<Object>(obj)));
}
std::string key("hi");
InsertObject(key, new Object());
or alternatively just:
void InsertObject(std::string handle, std::shared_ptr<Object> obj){
_obj.insert(std::make_pair(handle, obj));
}
Also note that std::map's indexing operator[] overwrites the old value if it exists, while the insert you're already using will only insert if the old one doesn't exist.

Trying to store objects in a vector

I'm quite new to C++ and I am trying to store objects inside a std::vector like this:
Event.h:
//event.h
class Event
{
public:
Event();
Event(std::string name);
~Event();
void addVisitor(Visitor visitor);
private:
std::vector<Visitor> m_visitors;
};
Event.cpp:
//event.cpp
Event::Event() :
m_name("Unnamed Event")
{
}
Event::Event(std::string name) :
m_name(name)
{
}
void Event::addVisitor(Visitor visitor)
{
this->m_visitors.push_back(visitor);
}
void Event::listVisitors()
{
std::vector<Visitor>::iterator it;
for(it = this->m_visitors.begin();it != this->m_visitors.end(); ++it)
{
std::cout << it->getName() << std::endl;
}
}
Visitor.h:
//visitor.h
class Visitor
{
public:
Visitor();
Visitor(std::string name);
~Visitor();
std::string getName() const;
void listVisitors();
private:
std::string m_name;
};
Visitor.cpp:
//visitor.cpp
Visitor::Visitor() :
m_name("John Doe")
{
}
Visitor::Visitor(std::string name) :
m_name(name)
{
}
std::string Visitor::getName() const
{
return m_name;
}
main.cpp:
//main.cpp
int main()
{
Event *e1 = new Event("Whatever");
Visitor *v1 = new Visitor("Dummy1");
Visitor *v2 = new Visitor("Dummy2");
e1->addVisitor(*v1);
e1->addVisitor(*v2);
}
If I do it like this I would have to add a copy constructor which would make a deep copy so the object gets copied properly into the vector. I'm looking for a way around it by only storing pointers to the objects in a vector.
I already tried it with std::vector<std::unique_ptr<Visitor> > m_visitors, but then I got some errors when calling addVisitor in main.cpp. Of course I changed the declaration of the class members accordingly.
How would an appropriate declaration of the members and the member function look like to make it work?
Stylistically, if you are passing pointers, just accept pointers as the function arguments.
What's happening in the example code above is that the visitors are getting copied to become function arguments and the pointers you had are unreferenced by anything outside of the main function.
I can't speak to what the errors are that you're seeing as you didn't describe them but it probably has to do with incompatible types.
Just get rid of the news because for these data structures they're unnecessary.
int main()
{
Event e1("Whatever");
Visitor v1("Dummy1");
Visitor v2("Dummy2");
e1.addVisitor(v1);
e1.addVisitor(v2);
}
I would suggest that if you don't know how to use pointers you couldn't possibly want to store them instead (they're a hassle IMO to store in the vector when copying by value works just fine).
The compiler generated copy constructor should work just fine.
No manual deep copy required, because you are quite correctly using std::string, which supports RAII.
However, your main function has three memory leaks — there is no need to use new there anyway, so simply don't.
General rule of thumb:
If, at any time T, you're thinking of introducing more pointers into your code, then you're probably going in the wrong direction.