I have a function that returns a Customer object (not pointer) like this:
Customer CustomerList::retrieve(const int index) const{
if (index<1 || index>size)
return false;
else{
Node *cur = find(index);
return (cur->data);
}
}
This function gets a Customer object from a CustomerList(which is a linkedlist).
I'm trying to manipulate the Customer in the list with following function(this function adds an Account to the Customer object.)
list.retrieve(i).addAccount(acc);
However after this function call, Customer object in CustomerList doesn't change. I assume that the reason is I return a copy of a Customer object, not the object itself.
So in order to return the adress of Customer and manipulate it correctly, i make the following changes to my function.
Customer* CustomerList::retrieve(const int index) const{
if (index<1 || index>size)
return false;
else{
Node *cur = find(index);
return &(cur->data);
}
}
And call the manipulating function like that:
list.retrieve(i)->addAccount(acc);
But it gives me a "Access violation reading location 0x00000044." error. What I want to learn is:
Why doesn't it manipulate the Customer object in first place? Is my assumption right?
After I change my functions and function calls, why does it gives me the error I mentioned above?
Why doesn't it manipulate the Customer object in first place? Is my assumption right?
As you say, you're returning a copy and manipulating that, leaving the one in the list untouched.
After I change my functions and function calls, why does it gives me the error I mentioned above?
Almost certainly because of this:
return false;
That will return a null pointer if the index is out of bounds. If that's the behaviour you want, then you'll need to check before dereferencing the pointer:
if (Customer * c = list.retrieve(i)) {
c->addAccount(acc);
} else {
// handle the error?
}
and, out of politeness, you should return something that looks more like a null pointer such as nullptr, NULL, or 0.
It might be a better idea to throw an exception (perhaps std::range_error); then the caller can assume that the pointer is valid if the function returns. In that case, it might also be better to return a reference rather than a pointer, giving code very much like your original example:
Customer & CustomerList::retrieve(const int index) const{
if (index<1 || index>size)
throw std::range_error("Customer index out of range");
else{
Node *cur = find(index);
return (cur->data);
}
}
list.retrieve(i).addAccount(acc); // Does exactly what you'd expect
I might also consider moving the range checks into the find function, if that seems appropriate.
Why doesn't it manipulate the Customer object in first place?
Yes you are right. by defualt its retuned by value not by reference so original object in List is not geting modified.
After I change my functions and function calls, why does it gives me the error I mentioned above?
I think you need to share the code of addAccount Method. the problem may be inside it.
Considering then with original code return by value it was working corectly (without exception).
Related
I am trying to write a function that receives a pointer, uses it, and then makes it point to a new object. In order to do this, I am using a ptr-to-ptr. This is how I validate the ptr-to-ptr received by my function:
void modifyPtr(Obj ** ptrToPtr)
{
if (*ptrToPtr == nullptr)
{
return;
}
else
{
// Do everything else!
}
}
While writing this, I thought: what if a client passes the following to my function?
Obj ** ptrToPtr = nullptr;
modifyPtr(ptrToPtr);
In that case, my validation will be dangerous, because I will be dereferencing a nullptr. So, should I add an additional step of validation?
void modifyPtr(Obj ** ptrToPtr)
{
if (ptrToPtr == nullptr)
{
return;
}
else if (*ptrToPtr == nullptr)
{
return;
}
else
{
// Do everything else!
}
}
I have never seen validation like this one before, which is why I am hesitant.
Please be aware that I know one should avoid using raw pointers in C++. I am working with old code, and I find this problem interesting.
Your first validation is wrong, because you dereference ptrToPtr before checking if it is null.
You probably don't need to check the dereferenced pointer for null since you are going to change it anyway (unless you need to do something with the old object).
However, you should prefer using references instead of double-pointers, eg:
void modifyPtr(Obj* &Ptr)
Then the caller can't pass in a null reference (without doing ugly hacks).
If you just return the new pointer instead of modifying the parameter, you can get away with just one level of indirection and simpler validation. The caller can decide if they want to reassign it immediately to the old value.
//use reference.
void modifyPtr(Obj *& ptrToPtr)
{
if (ptrToPtr == nullptr)
{
return;
}
else
{
// Do everything else!
}
}
Obj * ptrToPtr = nullptr;
modifyPtr(ptrToPtr);
In your first if you do not dereference anything. You just check if the passed pointer is null or not. So you do not have to worry. Just remove the * which i have not noticed on my mobile
I have to implement a two way ordered list at a specific way, and I got stuck. I have a Bag class that has an Element class inside it. Now the problem is when I want to place an item inside the bag.
An element has a key (its value), a pointer to the smaller element (_down), and one to the larger (_up). When I put a new element inside the bag the code is the following (names translated so it's easier to understand)
void Bag::put(int e) {
if(_item_count==0){
Element *element = new Element(e, nullptr, nullptr);
_bot = element;
_top = element;
}else{
Element *p = _bot;
while(e > p->key()){
p = p->up();
}
Element *element = new Element(e, p, p->down());
p->down() = element;
}
}
So the problem is that p->down() is not assignable. I guess I should return the pointer by reference, but I don't know how to do that.
down() function
Bag::Element *Bag::Element::down() {
return _down;
}
I tried putting & at great many places, but I just can't figure out how to make it all work.
Here's the full code if needed, but it's variables are mostly Hungarian, I'll provide translation if needed.
Header on the left, cpp on the right
This will return a non-const lvalue reference to pointer which is good to be assigned to:
Bag::Element *&Bag::Element::down() {
return _down;
}
And perhaps you should also provide a const overload:
Bag::Element * const &Bag::Element::down() const {
return _down;
}
Of course you should update your class declaration accordingly.
And also you may consider using a struct for such simple classes like Element while making pointer fields public (which is the default access control for structs. Typically you won't get bashed for doing this in C/C++, unlike in Java where people tend to insist on using private fields and getter/setters even for super simple classes. (I'm not saying this good or bad, just a sort of convention.)
Just assign the pointer directly:
p->_down = element;
Note that you are only modifying the "down" pointer of the next element. You also need to modify the "up" pointer of the previous element.
Note also that your new element may be the first or the last element in the list, you should handle these cases specially. Once you do that, you may discover that special handling of the empty list is unnecessary.
I’m working on a beginner(!) exercise.
I am comfortable passing basic variables and also using &variable parameters so I can make changes to the variable that are not destroyed when returning. But am still learning pointers. I am working on the basic Mutant Bunny exercise (linked list practice).
In it I create a linked list by declaring Class Bunny. I set it up as you expect with a data section and a ‘next’ pointer for set up the linkage.
struct Bunny {
string name;
int age;
// more variables here
Bunny* next;
};
Everything works great when I call function to do things like create Bunnies using the function:
Bunny* add_node ( Bunny* in_root ){}
This sets up the node and returns it just like I want. I can also do things like call a function to modify the Bunny class like aging the bunnies.
void advanceAge ( Bunny* in_root ){}
I pass in the head and then I can modify the bunnies in the called function and it stays modified even when it goes back to main. For example I can use:
in_root->age ++;
in the called function and when I return to ‘main’ it is still changed. Basically I can use -> in any called function and it makes the change permanently. I think because the pointer is dereferenced(?) by the -> but still getting my head around it...
So far so good.
The problem comes up when I want call a function to delete the list. (Nuclear option… no more bunnies)
I can delete all the nodes in the called function… but it does not change the Bunny in ‘main’. For example… this does not permanently remove the node.
void DeathCheck(Bunny* in_root){
Bunny* prev_ptr;
prev_ptr = in_root;
if (prev_ptr == NULL){
cout << "No list to check age." << endl; return;
} else {
prev_ptr = NULL; // <- what could I code to have this stick? return;}
// rest of DeathCheck
I’m curious if there is a way to set the node to NULL in the called function and have it stick?
Since you're passing in_root by value, there's no way for it to modify the caller's variable. You could pass it by reference.
void DeathCheck(Bunny* &in_root) {
Bunny *prev_ptr = in_root;
...
in_root = nullptr;
return;
}
Currently, in DeathCheck(Bunny* in_root), there is no way that in_root can be changed, only the object it is pointing to can be changed. (See pass by reference and value with pointers). Based on this, you need to change the parameter to pass-by reference, eg by changing the signature of your function to this:
DeathCheck(Bunny* &in_root)
{
//...
}
This passes the Bunny by reference, meaning that it can now be reassigned to without a copy.
I am not experienced enough in C/C++ programming, so I am asking for an explanation.
I have global array declared as following. ASAK it is located in seperate memory part of initialized global memory in context of process memory.
Sensor sensorsArray[SENSORS_COUNT] = {dhtTempSensor, dhtHumSensor, dallasTempSensor, waterLevelSensor};
I need to find element in this array and return pointer to it (because I am going to change its value). I have written such function.
Sensor* getSensorById(uint32_t id) {
for (int i = 0; i < SENSORS_COUNT; i++) {
Sensor* current = &sensorsArray[i];
if (current->sensorId == id) {
return current;
}
}
}
Will it work properly, I am not sure about current pointer, it is allocated on the stack so it is in function scope, will it be poped from the stack after function ends ? Or it will work properly.
I mean not pointer(address of array element which is taken using &sensorsArray[i]), but current pointer variable which contains address of erray element, will it be poped or not.
Please suggest best way how to do in such situation.
Thx.
You aren't covering all the possible returning cases of the function, namely, the case when the id does not match with any of the ids of the array.
Currently the pointer will return the last element of the array if there is no match.
You could correct that by defining the pointer Sensor* sensor_found = nullptr outside the for loop such that if there is no sensor found the return value is still valid, i.e. nullptr and assigning the found value of current to sensor_found, only if there is a match.
Sensor* getSensorById(uint32_t id) {
Sensor* sensor_found = nullptr;
for (int i = 0; i < SENSORS_COUNT; i++) {
Sensor* current = &sensorsArray[i];
if (current->sensorId == id) {
sensor_found = current;
break;
}
}
return sensor_found;
}
If the id found return current, otherwise, if there is no match return nullptr.
you want to make sure that the function has a valid return statement on its every execution path. In you current implementation if the id is not matched then the return value of Sensor* is not set and will contain random bytes. One wau to deal with this situation is to return the nullptr to indicate that the Sensor was not found. Other than that, ythe function will work properly.
Sensor* getSensorById(uint32_t id) {
for (int i = 0; i < SENSORS_COUNT; i++) {
Sensor* current = &sensorsArray[i];
if (current->sensorId == id) {
return current;
}
}
return nullptr; // id not matched
}
Your code is fine (as the comments suggest). The reason why you don't need to worry about the current pointer becoming invalid is because the memory that it points to (ie, the global array) stays valid beyond the scope of the function. Just because you happen to create a pointer (and remember, a pointer is really just a number that corresponds to some place in memory) to that memory doesn't mean that it becomes invalid when used elsewhere.
When you say Sensor *current = &sensorArray[i];, then if sensorArray[i] is stored at, say, position 0x10 in memory, then current = 0x10, and no matter where it is used, then sensorArray[i] will still be at memory location 0x10. When you assign a value to current, you are not copying the value from the sensor, you are merely getting a pointer to it.
I have created an object of a certain class. The class is "Node" and it has an attribute of CString strName. The value of this variable can be retrieved with a method of Node: CString Node::GetName(), which just returns the name of the variable.
In the following method I instantiate this:
Node* UpperClass::GetObject(CString value) {
Node retObject;
retObject.strName = value;
Trace(retObject.strName); // Prints argument to trace file - this prints the value of strName fine
return &retObject;
}
Then I run this method in a second class:
Node* LowerClass::Get() {
Node *pReturn = instanceOfUpperClass.GetObject();
Trace(pReturn->GetName()); // This trace just prints blank...
return *(&pReturn);
}
As you can see by the code comments, it seems to lose the value when it is passed to the second method. I've attempted to research this but am having some real trouble getting to grips with why.. can anyone help?
As an aside, if one is wondering about the return value of the second method, I am intending to past the resultant pointer to a third function where I utilise it for processing (messy, I know, but I inherited the code and have no choice); just in case that has any bearing on the answer.
Thanks in advance!
int this method:
Node* UpperClass::GetObject(CString value) {
Node retObject;
retObject.strName = value;
Trace(retObject.strName); // Prints argument to trace file - this prints the value of strName fine
return &retObject; // <-- Undefined Baaviour
}
you are returning pointer to local object, which is destroyed once this method ends. You should create retObject dynamically, and return pointer to it, or better put this pointer into shared_ptr<>.
The Node retObject is a local variable when you go out of scope of the function GetObject any use of the object is undefined. And in this case you return a pointer to that object.