Validating a pointer to a pointer in C++ - c++

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

Related

How to properly return null/empty object in C++?

I am a Java developer trying to solve a simple C++ task. In Java, if I want to return an empty/null object, to indicate that object is not found I just return null. Example:
Person getGetByName(String name) {
for (int i = 0; i < 10; i++) {
if (people[i].name == name) {
return people[i];
}
}
return null;
}
later in code I can do
Person p = getByName("Sam");
if (p == null) { ...
}
In C++ I have the following method
It seems that if I declare a method like the above, returning NULL is not an option since the method doesn't return a pointer but rather a class.
So what do I return to indicate that no person is found ?
If you're using C++17 or above, use std::optional.
Alternatively, return a value that would convey a similar meaning, e.g. a std::pair<bool, Person> Note that this would still require a Person object to be there, it merely does not need to be valid. If the objects are too large to be returned emptily like that, you'll have to find a workaround, like a union, or a pointer.
There's one more problem to the above code - the C++ version is inefficient. In C++, returning Person by-value always returns a copy of it (except for RVO, which is not applicable here).
Unlike C++, Java has no value semantics for objects, in Java returning Person always returns a reference to an object. A reference in Java works more like a pointer in C++, e.g. it can be null.
So the equivalent C++ code would actually be:
Person* getGetByName(std::string const& name) {
for(int i = 0 ;i<10 ;i++) {
if(people[i].name == name){
return &people[i];
}
}
return nullptr;
}
Now you can do
Person* p = getGetByName("blah");
if (p == nullptr) {
// not found ...
} else {
// use p->name etc.
}
C++ has no automatic memory management. The lifetime of the people vector must thus outlast the returned pointer. If that's not the case then it's more idiomatic to work with smart pointers instead (e.g. unique_ptr or shared_ptr). Thinking about object lifetimes is the responsibility of the C++ developer.
You could return the person object through a parameter and return a bool to indicate if something meaningful was returned through the parameter:
bool getGetByName(const std::string& name, Person& person)
{
for(int i = 0; i < 10 ;i++)
{
if(people[i].name == name)
{
person = person[i] // Writing in the person param (note the reference in the definition of the 'person' parameter).
return true; // Indicate success (i.e. non null).
}
}
return false; // Indicates null.
}
Later in code you could write:
Person aPerson;
if(getGetByName("A name", aPerson))
{
// aPerson is valid, do something with it.
}
// aPerson is not valid, handle it.
In Java, class instance variables and such returned objects are actually references/pointers. Since they are pointers, they can point to null. Primitive types such as int on the other hand are not pointers. Since they aren't pointers, they cannot point to null. You cannot have int x = null;.
In C++, class instance variables and such returned objects are not pointers. Like the Java primitives, they cannot point to null.
How to properly return null/empty object in C++?
If the type of the object has a value that represents null or empty, then simply return that value. Here is an example where that type is a pointer:
int* function()
{
return nullptr;
}
When a type has an empty value, such value can usually (but not necessarily) be created using value initialisation. Here is an example that returns an empty vector:
std::vector<int> function()
{
return {};
}
If the type doesn't have a representation for null or empty, then you cannot return a null or empty value that doesn't exist.
However, using type erasure techniques, it is possible to design a class that internally contains either a value of another type, or doesn't. Using a template, such class can be used to augment any type with an empty value. The standard library comes with such template: std::optional.
As for your particular example, idiomatic solution in C++ is to return an iterator to the found element, and return an iterator to the (one past the) end of the range if nothing is found. Iterator is a generalisation of a pointer. Your example re-written in C++:
auto getGetByName(std::string_view name) {
auto first = std::begin(people);
auto last = std::end(people);
for (; first != last; ++first) {
if (*first == name) {
return first;
}
}
return last;
}
// later
auto it = getByName("Sam");
if (it == std::end(people)) { ...
}
Note that there is no need to write that function, since the C++ standard library provides implementation of linear search. Simply call:
auto it = std::ranges::find(people, "Sam");

Delete mulitple pointers in a method of another class

I am editing some code of an open source game and normally the code doesn't directly access the player or creature class; however its parameter Cylinder is at the top of the food chain when it comes to everything.
My question is should I be deleting all these pointers or setting them to NULL after I am done with them?
Here is the code I've written; it works fine but I don't want to crash the server over an issue such as a dangling pointer (still a bit new to C++).
bool Game::removeMoney(Cylinder* cylinder, uint64_t money, uint32_t flags /*= 0*/)
{
if (cylinder == nullptr) {
return false;
}
if (money == 0) {
return true;
}
if (Creature *creature = cylinder->getCreature()) {
if (Player *player = creature->getPlayer()) {
uint64_t cash = player->getBankBalance();
if (cash < money) {
return false;
}
player->setBankBalance(cash - money);
}
}
return true;
}
void Game::addMoney(Cylinder* cylinder, uint64_t money, uint32_t flags /*= 0*/)
{
if (Creature *creature = cylinder->getCreature()) {
if (Player *player = creature->getPlayer()) {
player->setBankBalance(player->getBankBalance() + money);
}
}
}
In general (and unless the documentation says otherwise), don't delete objects if you are passed a pointer. Assume that you are not being given ownership of the object.
Modern C++ helps you avoid needing to know whether you are being given ownership: you may be given a std::shared_ptr<Cylinder> or a std::unique_ptr<Cylinder> - either way, deletion is handled for you when the smart pointer goes out of scope. But often, you have to work with a library that doesn't give you such reassurance.
There's no need to null out any pointers used within a small scope (e.g. a function). If you keep pointer variables around for longer (in a member variable, perhaps), then it may help prevent accidents if you do so. As C++ is not a garbage-collected language, there's no benefit from nulling pointers that are about to go out of scope.
delete is only required if there is a call to new when you obtain the Cylinder object from the game. There probably isn't, but you need to check the code.
Setting to NULL is something that you do if the object pointed to has been (or is at risk of getting) deleted. This is only to ensure that the invalid pointer cannot be accidentally used some time later.

How do i delete a class node with pointer chidren

I am having errors deleting the private member class called tree2, I have tried to use "**", "&*", "*&" but I just keep getting error after error.
header file:
class tree1
{
private:
class tree2
{
tree2*child;
tree2**child2;
int data;
};
void clear( tree2** the_root);
tree2* root;
};
I am the one who has put the clear function there.So I go in the .cpp file and implement it this way:
void tree1::clear(tree2** TheRoot)
{
if(*TheRoot == NULL) { return; }
clear(&(*TheRoot->child1));
clear(&(*TheRoot->child2));
delete TheRoot;
TheRoot = NULL;
}
then in a function that used clear, i call it as clear(root) or clear(&root) or clear(*root) or clear(&*root).All combinations have failed, i keep getting erros. What is the right way to delete this class ?
As it seems you want your root-Pointer to be NULL after deletion. That is the reason why just passing tree2* as a parameter is not sufficient and the tree2** is necessary.
The line delete TheRoot; will not delete root, but a pointer to root (which was not allocated via new in your example, thus causing some hidden error. The same problem is in the next line. You can solve this by writing delete *TheRoot; *TheRoot = NULL;.
But since you are using C++, you can pass tree2*& like so:
void tree1::clear(tree2*& TheRoot)
{
if (TheRoot == NULL) { return; }
clear(TheRoot->child1);
clear(TheRoot->child2);
delete TheRoot;
TheRoot = NULL;
}
and call it like clear(root);
you need to call it with clear(&root) if you want to modify the value. In side, you do *root = null to clear it.
It is sufficient to use just tree2 and tree2* (pointer to a tree2 type) data types, do not use tree2** (pointer to a pointer of ..).
Do not expect the pointed by element to be NULL, but set the pointer value to NULL and just check the pointer values to decide if they point to allocated memory or not.

calling a function through a pointer - access violation reading location

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).

c++ trouble with what parameter to send

I'm currently coding on a project and I have a function that looks like this:
Room::addItem(Item*&); //not written by me
I have some trouble understanding what to send as parameter.. the "*&" mess it up for me.
I've tried the following:
foo.addItem(loadItem()); //Returns an Item-object
/*and*/
foo.addItem(loadItem()); //Returns an Item-pointer
edit: It would be nice if you explain what the "*&" means. I want to understand it next time I run in to it ;)
The addItem function accepts argument of type Item* and the pointer is passed by reference. It means that the function addItem can modify the pointer internally. This may also imply that the object is being reallocated or modified inside this function.
Example:
void pointerByValue(int* ptr)
{
ptr = new int[10];
}
void pointerByReference(int*& ptr)
{
ptr = new int[10];
}
void main()
{
int* p = NULL; //A NULL pointer
pointerByValue(p); //p is still NULL
pointerByReference(p); //memory has now been allocated to p
}
Pointer By Reference are only valid in C++.
It looks to me like your function is expecting a reference to a pointer. For example, MSDN has some sample code with similar syntax.
// Add2: Add a node to the binary tree.
// Uses reference to pointer
int Add2( BTree*& Root, char *szToAdd ) {
if ( Root == 0 ) {
...
There are various reasons why you might want to do that, but your favorite search engine should be able to help you there. One blog entry to point you in the right direction is here.
The parameter type is a reference to a pointer to an Item, and
what you need to pass to it is a pointer to an Item (i.e.
Item*), which must, in addition, be an lvalue (because presumably,
Room::addItem is going to modify the pointer).
you need to pass pointer to function
Item item = loadItem()
foo.addItem(&item);
& means that function will use reference and will be able to change it value